diff options
Diffstat (limited to 'sys/arch/wgrisc/dev')
-rw-r--r-- | sys/arch/wgrisc/dev/ace.c | 1653 | ||||
-rw-r--r-- | sys/arch/wgrisc/dev/asc.c | 2096 | ||||
-rw-r--r-- | sys/arch/wgrisc/dev/ascreg.h | 326 | ||||
-rw-r--r-- | sys/arch/wgrisc/dev/com_risc.c | 195 | ||||
-rw-r--r-- | sys/arch/wgrisc/dev/dma.h | 147 | ||||
-rw-r--r-- | sys/arch/wgrisc/dev/if_sn.c | 1288 | ||||
-rw-r--r-- | sys/arch/wgrisc/dev/if_sn.h | 347 | ||||
-rw-r--r-- | sys/arch/wgrisc/dev/scsi.h | 559 |
8 files changed, 6611 insertions, 0 deletions
diff --git a/sys/arch/wgrisc/dev/ace.c b/sys/arch/wgrisc/dev/ace.c new file mode 100644 index 00000000000..d577033f2c7 --- /dev/null +++ b/sys/arch/wgrisc/dev/ace.c @@ -0,0 +1,1653 @@ +/* $OpenBSD: ace.c,v 1.1 1997/02/06 16:02:42 pefo Exp $ */ +/* $NetBSD: com.c,v 1.82.4.1 1996/06/02 09:08:00 mrg Exp $ */ + +/*- + * Copyright (c) 1993, 1994, 1995, 1996 + * Charles M. Hannum. All rights reserved. + * Copyright (c) 1991 The Regents of the University of California. + * 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 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. + * + * @(#)com.c 7.5 (Berkeley) 5/16/91 + */ + +/* + * ACE driver, based on HP dca driver + * uses National Semiconductor NS16450/NS16550AF UART + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/ioctl.h> +#include <sys/select.h> +#include <sys/tty.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/uio.h> +#include <sys/kernel.h> +#include <sys/syslog.h> +#include <sys/types.h> +#include <sys/device.h> + +#include <wgrisc/wgrisc/wgrisctype.h> +#include <machine/bus.h> +#include <machine/intr.h> + +#include <dev/isa/isavar.h> +#include <dev/ic/comreg.h> +#include <dev/ic/comvar.h> +#include <dev/ic/ns16550reg.h> +#ifdef COM_HAYESP +#include <dev/ic/hayespreg.h> +#endif +#define com_lcr com_cfcr + +#include "ace.h" + + +#define COM_IBUFSIZE (2 * 512) +#define COM_IHIGHWATER ((3 * COM_IBUFSIZE) / 4) + +struct com_softc { + struct device sc_dev; + void *sc_ih; + bus_chipset_tag_t sc_bc; + struct tty *sc_tty; + + int sc_overflows; + int sc_floods; + int sc_errors; + + int sc_halt; + + int sc_iobase; +#ifdef COM_HAYESP + int sc_hayespbase; +#endif + + bus_io_handle_t sc_ioh; + bus_io_handle_t sc_hayespioh; + + u_char sc_hwflags; +#define COM_HW_NOIEN 0x01 +#define COM_HW_FIFO 0x02 +#define COM_HW_HAYESP 0x04 +#define COM_HW_ABSENT_PENDING 0x08 /* reattached, awaiting close/reopen */ +#define COM_HW_ABSENT 0x10 /* configure actually failed, or removed */ +#define COM_HW_REATTACH 0x20 /* reattaching */ +#define COM_HW_CONSOLE 0x40 + u_char sc_swflags; +#define COM_SW_SOFTCAR 0x01 +#define COM_SW_CLOCAL 0x02 +#define COM_SW_CRTSCTS 0x04 +#define COM_SW_MDMBUF 0x08 + u_char sc_msr, sc_mcr, sc_lcr, sc_ier; + u_char sc_dtr; + + u_char sc_cua; + + u_char sc_initialize; /* force initialization */ + + u_char *sc_ibuf, *sc_ibufp, *sc_ibufhigh, *sc_ibufend; + u_char sc_ibufs[2][COM_IBUFSIZE]; +}; + +#ifdef COM_HAYESP +int comprobeHAYESP __P((bus_io_handle_t hayespioh, struct com_softc *sc)); +#endif +void comdiag __P((void *)); +int comspeed __P((long)); +int comparam __P((struct tty *, struct termios *)); +void comstart __P((struct tty *)); +void compoll __P((void *)); + +/* XXX: These belong elsewhere */ +cdev_decl(com); +bdev_decl(com); + +struct consdev; +void acecnprobe __P((struct consdev *)); +void acecninit __P((struct consdev *)); +int acecngetc __P((dev_t)); +void acecnputc __P((dev_t, int)); +void acecnpollc __P((dev_t, int)); + +static u_char tiocm_xxx2mcr __P((int)); + +/* + * XXX the following two cfattach structs should be different, and possibly + * XXX elsewhere. + */ +int comprobe __P((struct device *, void *, void *)); +void comattach __P((struct device *, struct device *, void *)); +void com_absent_notify __P((struct com_softc *sc)); +void comstart_pending __P((void *)); + +#if NACE_ISA +struct cfattach ace_isa_ca = { + sizeof(struct com_softc), comprobe, comattach +}; +#endif + +#if NACE_COMMULTI +struct cfattach ace_commulti_ca = { + sizeof(struct com_softc), comprobe, comattach +}; +#endif + +#if NACE_RISCBUS +#undef CONADDR /* This is stupid but using devs before config .. */ +#define CONADDR 0xae400000 + +struct cfattach ace_riscbus_ca = { + sizeof(struct com_softc), comprobe, comattach +}; +#endif + + +struct cfdriver ace_cd = { + NULL, "ace", DV_TTY +}; + +void cominit __P((bus_chipset_tag_t, bus_io_handle_t, int)); + +#ifndef CONSPEED +#define CONSPEED B9600 +#endif + +#ifdef COMCONSOLE +int comdefaultrate = CONSPEED; /* XXX why set default? */ +#else +int comdefaultrate = TTYDEF_SPEED; +#endif +int comconsaddr; +int comconsinit; +int comconsattached; +bus_chipset_tag_t comconsbc; +bus_io_handle_t comconsioh; +tcflag_t comconscflag = TTYDEF_CFLAG; + +int commajor; +int comsopen = 0; +int comevents = 0; + +#ifdef KGDB +#include <machine/remote-sl.h> +extern int kgdb_dev; +extern int kgdb_rate; +extern int kgdb_debug_init; +#endif + +#define DEVUNIT(x) (minor(x) & 0x7f) +#define DEVCUA(x) (minor(x) & 0x80) + +/* Macros to clear/set/test flags. */ +#define SET(t, f) (t) |= (f) +#define CLR(t, f) (t) &= ~(f) +#define ISSET(t, f) ((t) & (f)) + +/* + * must be called at spltty() or higher. + */ +void +com_absent_notify(sc) + struct com_softc *sc; +{ + struct tty *tp = sc->sc_tty; + + if (tp) { + CLR(tp->t_state, TS_CARR_ON|TS_BUSY); + ttyflush(tp, FREAD|FWRITE); + } +} + +int +comspeed(speed) + long speed; +{ +#define divrnd(n, q) (((n)*2/(q)+1)/2) /* divide and round off */ + + int x, err; + + if (speed == 0) + return 0; + if (speed < 0) + return -1; + x = divrnd((COM_FREQ / 16), speed); + if (x <= 0) + return -1; + err = divrnd((COM_FREQ / 16) * 1000, speed * x) - 1000; + if (err < 0) + err = -err; + if (err > COM_TOLERANCE) + return -1; + return x; + +#undef divrnd(n, q) +} + +int +comprobe1(bc, ioh, iobase) + bus_chipset_tag_t bc; + bus_io_handle_t ioh; + int iobase; +{ + int i, k; + + /* force access to id reg */ + bus_io_write_1(bc, ioh, com_lcr, 0); + bus_io_write_1(bc, ioh, com_iir, 0); + for (i = 0; i < 32; i++) { + k = bus_io_read_1(bc, ioh, com_iir); + if (k & 0x38) { + bus_io_read_1(bc, ioh, com_data); /* cleanup */ + } else + break; + } + if (i >= 32) + return 0; + + return 1; +} + +#ifdef COM_HAYESP +int +comprobeHAYESP(hayespioh, sc) + bus_io_handle_t hayespioh; + struct com_softc *sc; +{ + char val, dips; + int combaselist[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; + bus_chipset_tag_t bc = sc->sc_bc; + + /* + * Hayes ESP cards have two iobases. One is for compatibility with + * 16550 serial chips, and at the same ISA PC base addresses. The + * other is for ESP-specific enhanced features, and lies at a + * different addressing range entirely (0x140, 0x180, 0x280, or 0x300). + */ + + /* Test for ESP signature */ + if ((bus_io_read_1(bc, hayespioh, 0) & 0xf3) == 0) + return 0; + + /* + * ESP is present at ESP enhanced base address; unknown com port + */ + + /* Get the dip-switch configurations */ + bus_io_write_1(bc, hayespioh, HAYESP_CMD1, HAYESP_GETDIPS); + dips = bus_io_read_1(bc, hayespioh, HAYESP_STATUS1); + + /* Determine which com port this ESP card services: bits 0,1 of */ + /* dips is the port # (0-3); combaselist[val] is the com_iobase */ + if (sc->sc_iobase != combaselist[dips & 0x03]) + return 0; + + printf(": ESP"); + + /* Check ESP Self Test bits. */ + /* Check for ESP version 2.0: bits 4,5,6 == 010 */ + bus_io_write_1(bc, hayespioh, HAYESP_CMD1, HAYESP_GETTEST); + val = bus_io_read_1(bc, hayespioh, HAYESP_STATUS1); /* Clear reg 1 */ + val = bus_io_read_1(bc, hayespioh, HAYESP_STATUS2); + if ((val & 0x70) < 0x20) { + printf("-old (%o)", val & 0x70); + /* we do not support the necessary features */ + return 0; + } + + /* Check for ability to emulate 16550: bit 8 == 1 */ + if ((dips & 0x80) == 0) { + printf(" slave"); + /* XXX Does slave really mean no 16550 support?? */ + return 0; + } + + /* + * If we made it this far, we are a full-featured ESP v2.0 (or + * better), at the correct com port address. + */ + + SET(sc->sc_hwflags, COM_HW_HAYESP); + printf(", 1024 byte fifo\n"); + return 1; +} +#endif + +int +comprobe(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + bus_chipset_tag_t bc; + bus_io_handle_t ioh; + int iobase, needioh; + int rv = 1; + +#if NACE_ISA +#define IS_ISA(parent) \ + (!strcmp((parent)->dv_cfdata->cf_driver->cd_name, "isa") || \ + !strcmp((parent)->dv_cfdata->cf_driver->cd_name, "pcmcia")) +#elif NACE_ISA +#define IS_ISA(parent) \ + !strcmp((parent)->dv_cfdata->cf_driver->cd_name, "isa") +#endif +#if NACE_RISCBUS +#define IS_RISCBUS(parent) \ + !strcmp((parent)->dv_cfdata->cf_driver->cd_name, "riscbus") +#endif + /* + * XXX should be broken out into functions for isa probe and + * XXX for commulti probe, with a helper function that contains + * XXX most of the interesting stuff. + */ +#if NACE_ISA + if (IS_ISA(parent)) { + struct isa_attach_args *ia = aux; + + bc = ia->ia_bc; + iobase = ia->ia_iobase; + needioh = 1; + } else +#endif +#if NACE_RISCBUS + if(IS_RISCBUS(parent)) { + struct confargs *ca = aux; + if(!BUS_MATCHNAME(ca, "com")) + return(0); + iobase = (long)BUS_CVTADDR(ca); + bc = 0; + needioh = 1; + } else +#endif +#if NACE_COMMULTI + if (1) { + struct cfdata *cf = match; + struct commulti_attach_args *ca = aux; + + if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != ca->ca_slave) + return (0); + + bc = ca->ca_bc; + iobase = ca->ca_iobase; + ioh = ca->ca_ioh; + needioh = 0; + } else +#endif + return(0); /* This cannot happen */ + + /* if it's in use as console, it's there. */ + if (iobase == comconsaddr && !comconsattached) + goto out; + + if (needioh && bus_io_map(bc, iobase, COM_NPORTS, &ioh)) { + rv = 0; + goto out; + } + rv = comprobe1(bc, ioh, iobase); + if (needioh) + bus_io_unmap(bc, ioh, COM_NPORTS); + +out: +#if NACE_ISA + if (rv && IS_ISA(parent)) { + struct isa_attach_args *ia = aux; + + ia->ia_iosize = COM_NPORTS; + ia->ia_msize = 0; + } +#endif + return (rv); +} + +void +comattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct com_softc *sc = (void *)self; + int iobase, irq; + bus_chipset_tag_t bc; + bus_io_handle_t ioh; +#ifdef COM_HAYESP + int hayesp_ports[] = { 0x140, 0x180, 0x280, 0x300, 0 }; + int *hayespp; +#endif + + /* + * XXX should be broken out into functions for isa attach and + * XXX for commulti attach, with a helper function that contains + * XXX most of the interesting stuff. + */ + if (ISSET(sc->sc_hwflags, COM_HW_REATTACH)) { + int s; + s = spltty(); + com_absent_notify(sc); + splx(s); + } else + sc->sc_hwflags = 0; + sc->sc_swflags = 0; +#if NACE_ISA + if (IS_ISA(parent)) { + struct isa_attach_args *ia = aux; + + /* + * We're living on an isa. + */ + iobase = ia->ia_iobase; + bc = ia->ia_bc; + if (iobase != comconsaddr) { + if (bus_io_map(bc, iobase, COM_NPORTS, &ioh)) + panic("comattach: io mapping failed"); + } else + ioh = comconsioh; + irq = ia->ia_irq; + } else +#endif +#if NACE_RISCBUS + if(IS_RISCBUS(parent)) { + struct confargs *ca = aux; + iobase = (long)BUS_CVTADDR(ca); + bc = 0; + irq = 0; + ioh = iobase; + } else +#endif +#if NACE_COMMULTI + if (1) { + struct commulti_attach_args *ca = aux; + + /* + * We're living on a commulti. + */ + iobase = ca->ca_iobase; + bc = ca->ca_bc; + ioh = ca->ca_ioh; + irq = IRQUNK; + + if (ca->ca_noien) + SET(sc->sc_hwflags, COM_HW_NOIEN); + } else +#endif + panic("comattach: impossible"); + + sc->sc_bc = bc; + sc->sc_ioh = ioh; + sc->sc_iobase = iobase; + + if (iobase == comconsaddr) { + comconsattached = 1; + + /* + * Need to reset baud rate, etc. of next print so reset + * comconsinit. Also make sure console is always "hardwired". + */ + delay(1000); /* wait for output to finish */ + comconsinit = 0; + SET(sc->sc_hwflags, COM_HW_CONSOLE); + SET(sc->sc_swflags, COM_SW_SOFTCAR); + } + +#ifdef COM_HAYESP + /* Look for a Hayes ESP board. */ + for (hayespp = hayesp_ports; *hayespp != 0; hayespp++) { + bus_io_handle_t hayespioh; + +#define HAYESP_NPORTS 8 /* XXX XXX XXX ??? ??? ??? */ + if (bus_io_map(bc, *hayespp, HAYESP_NPORTS, &hayespioh)) + continue; + if (comprobeHAYESP(hayespioh, sc)) { + sc->sc_hayespbase = *hayespp; + sc->sc_hayespioh = hayespioh; + break; + } + bus_io_unmap(bc, hayespioh, HAYESP_NPORTS); + } + /* No ESP; look for other things. */ + if (*hayespp == 0) { +#endif + + /* look for a NS 16550AF UART with FIFOs */ + bus_io_write_1(bc, ioh, com_fifo, + FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_14); + delay(100); + if (ISSET(bus_io_read_1(bc, ioh, com_iir), IIR_FIFO_MASK) == + IIR_FIFO_MASK) + if (ISSET(bus_io_read_1(bc, ioh, com_fifo), FIFO_TRIGGER_14) == + FIFO_TRIGGER_14) { + SET(sc->sc_hwflags, COM_HW_FIFO); + printf(": ns16550a, working fifo\n"); + } else + printf(": ns16550, broken fifo\n"); + else + printf(": ns8250 or ns16450, no fifo\n"); + bus_io_write_1(bc, ioh, com_fifo, 0); +#ifdef COM_HAYESP + } +#endif + + /* disable interrupts */ + bus_io_write_1(bc, ioh, com_ier, 0); + bus_io_write_1(bc, ioh, com_mcr, 0); + + if (irq != IRQUNK) { +#if NACE_ISA + if (IS_ISA(parent)) { + struct isa_attach_args *ia = aux; + + sc->sc_ih = isa_intr_establish(ia->ia_ic, irq, + IST_EDGE, IPL_TTY, comintr, sc, + sc->sc_dev.dv_xname); + } else +#endif +#if NACE_RISCBUS + if (IS_RISCBUS(parent)) { + struct confargs *ca = aux; + BUS_INTR_ESTABLISH(ca, comintr, (void *)(long)sc); + } else +#endif + panic("comattach: IRQ but can't have one"); + } + +#ifdef KGDB + if (kgdb_dev == makedev(commajor, unit)) { + if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) + kgdb_dev = -1; /* can't debug over console port */ + else { + cominit(bc, ioh, kgdb_rate); + if (kgdb_debug_init) { + /* + * Print prefix of device name, + * let kgdb_connect print the rest. + */ + printf("%s: ", sc->sc_dev.dv_xname); + kgdb_connect(1); + } else + printf("%s: kgdb enabled\n", + sc->sc_dev.dv_xname); + } + } +#endif + + /* XXX maybe move up some? */ + if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) + printf("%s: console\n", sc->sc_dev.dv_xname); +} + +int +aceopen(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ + int unit = DEVUNIT(dev); + struct com_softc *sc; + bus_chipset_tag_t bc; + bus_io_handle_t ioh; + struct tty *tp; + int s; + int error = 0; + + if (unit >= ace_cd.cd_ndevs) + return ENXIO; + sc = ace_cd.cd_devs[unit]; + if (!sc || ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) + return ENXIO; + + s = spltty(); + if (!sc->sc_tty) { + tp = sc->sc_tty = ttymalloc(); + tty_attach(tp); + } else + tp = sc->sc_tty; + splx(s); + + tp->t_oproc = comstart; + tp->t_param = comparam; + tp->t_dev = dev; + if (!ISSET(tp->t_state, TS_ISOPEN)) { + SET(tp->t_state, TS_WOPEN); + ttychars(tp); + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) + tp->t_cflag = comconscflag; + else + tp->t_cflag = TTYDEF_CFLAG; + if (ISSET(sc->sc_swflags, COM_SW_CLOCAL)) + SET(tp->t_cflag, CLOCAL); + if (ISSET(sc->sc_swflags, COM_SW_CRTSCTS)) + SET(tp->t_cflag, CRTSCTS); + if (ISSET(sc->sc_swflags, COM_SW_MDMBUF)) + SET(tp->t_cflag, MDMBUF); + tp->t_lflag = TTYDEF_LFLAG; + tp->t_ispeed = tp->t_ospeed = comdefaultrate; + + s = spltty(); + + sc->sc_initialize = 1; + comparam(tp, &tp->t_termios); + ttsetwater(tp); + + if (comsopen++ == 0) + timeout(compoll, NULL, 1); + + sc->sc_ibufp = sc->sc_ibuf = sc->sc_ibufs[0]; + sc->sc_ibufhigh = sc->sc_ibuf + COM_IHIGHWATER; + sc->sc_ibufend = sc->sc_ibuf + COM_IBUFSIZE; + + bc = sc->sc_bc; + ioh = sc->sc_ioh; +#ifdef COM_HAYESP + /* Setup the ESP board */ + if (ISSET(sc->sc_hwflags, COM_HW_HAYESP)) { + bus_io_handle_t hayespioh = sc->sc_hayespioh; + + bus_io_write_1(bc, ioh, com_fifo, + FIFO_DMA_MODE|FIFO_ENABLE| + FIFO_RCV_RST|FIFO_XMT_RST|FIFO_TRIGGER_8); + + /* Set 16550 compatibility mode */ + bus_io_write_1(bc, hayespioh, HAYESP_CMD1, HAYESP_SETMODE); + bus_io_write_1(bc, hayespioh, HAYESP_CMD2, + HAYESP_MODE_FIFO|HAYESP_MODE_RTS| + HAYESP_MODE_SCALE); + + /* Set RTS/CTS flow control */ + bus_io_write_1(bc, hayespioh, HAYESP_CMD1, HAYESP_SETFLOWTYPE); + bus_io_write_1(bc, hayespioh, HAYESP_CMD2, HAYESP_FLOW_RTS); + bus_io_write_1(bc, hayespioh, HAYESP_CMD2, HAYESP_FLOW_CTS); + + /* Set flow control levels */ + bus_io_write_1(bc, hayespioh, HAYESP_CMD1, HAYESP_SETRXFLOW); + bus_io_write_1(bc, hayespioh, HAYESP_CMD2, + HAYESP_HIBYTE(HAYESP_RXHIWMARK)); + bus_io_write_1(bc, hayespioh, HAYESP_CMD2, + HAYESP_LOBYTE(HAYESP_RXHIWMARK)); + bus_io_write_1(bc, hayespioh, HAYESP_CMD2, + HAYESP_HIBYTE(HAYESP_RXLOWMARK)); + bus_io_write_1(bc, hayespioh, HAYESP_CMD2, + HAYESP_LOBYTE(HAYESP_RXLOWMARK)); + } else +#endif + if (ISSET(sc->sc_hwflags, COM_HW_FIFO)) { + /* + * (Re)enable and drain FIFOs. + * + * Certain SMC chips cause problems if the FIFOs are + * enabled while input is ready. Turn off the FIFO + * if necessary to clear the input. Test the input + * ready bit after enabling the FIFOs to handle races + * between enabling and fresh input. + * + * Set the FIFO threshold based on the receive speed. + */ + for (;;) { + bus_io_write_1(bc, ioh, com_fifo, 0); + delay(100); + (void) bus_io_read_1(bc, ioh, com_data); + bus_io_write_1(bc, ioh, com_fifo, + FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | + (tp->t_ispeed <= 1200 ? + FIFO_TRIGGER_1 : FIFO_TRIGGER_8)); + delay(100); + if(!ISSET(bus_io_read_1(bc, ioh, + com_lsr), LSR_RXRDY)) + break; + } + } + + /* flush any pending I/O */ + while (ISSET(bus_io_read_1(bc, ioh, com_lsr), LSR_RXRDY)) + (void) bus_io_read_1(bc, ioh, com_data); + /* you turn me on, baby */ + sc->sc_mcr = MCR_DTR | MCR_RTS; + if (!ISSET(sc->sc_hwflags, COM_HW_NOIEN)) + SET(sc->sc_mcr, MCR_IENABLE); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + sc->sc_ier = IER_ERXRDY | IER_ERLS | IER_EMSC; + bus_io_write_1(bc, ioh, com_ier, sc->sc_ier); + + sc->sc_msr = bus_io_read_1(bc, ioh, com_msr); + if (ISSET(sc->sc_swflags, COM_SW_SOFTCAR) || DEVCUA(dev) || + ISSET(sc->sc_msr, MSR_DCD) || ISSET(tp->t_cflag, MDMBUF)) + SET(tp->t_state, TS_CARR_ON); + else + CLR(tp->t_state, TS_CARR_ON); + } else if (ISSET(tp->t_state, TS_XCLUDE) && p->p_ucred->cr_uid != 0) + return EBUSY; + else + s = spltty(); + + if (DEVCUA(dev)) { + if (ISSET(tp->t_state, TS_ISOPEN)) { + /* Ah, but someone already is dialed in... */ + splx(s); + return EBUSY; + } + sc->sc_cua = 1; /* We go into CUA mode */ + } + + /* wait for carrier if necessary */ + if (ISSET(flag, O_NONBLOCK)) { + if (!DEVCUA(dev) && sc->sc_cua) { + /* Opening TTY non-blocking... but the CUA is busy */ + splx(s); + return EBUSY; + } + } else { + while (!(DEVCUA(dev) && sc->sc_cua) && + !ISSET(tp->t_cflag, CLOCAL) && + !ISSET(tp->t_state, TS_CARR_ON)) { + SET(tp->t_state, TS_WOPEN); + error = ttysleep(tp, &tp->t_rawq, TTIPRI | PCATCH, + ttopen, 0); + if (error) { + /* XXX should turn off chip if we're the + only waiter */ + if (DEVCUA(dev)) + sc->sc_cua = 0; + splx(s); + return error; + } + } + } + splx(s); + + return (*linesw[tp->t_line].l_open)(dev, tp); +} + +int +aceclose(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ + int unit = DEVUNIT(dev); + struct com_softc *sc = ace_cd.cd_devs[unit]; + struct tty *tp = sc->sc_tty; + bus_chipset_tag_t bc = sc->sc_bc; + bus_io_handle_t ioh = sc->sc_ioh; + int s; + + /* XXX This is for cons.c. */ + if (!ISSET(tp->t_state, TS_ISOPEN)) + return 0; + + (*linesw[tp->t_line].l_close)(tp, flag); + s = spltty(); + if (!ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + /* can't do any of this stuff .... */ + CLR(sc->sc_lcr, LCR_SBREAK); + bus_io_write_1(bc, ioh, com_lcr, sc->sc_lcr); + bus_io_write_1(bc, ioh, com_ier, 0); + if (ISSET(tp->t_cflag, HUPCL) && + !ISSET(sc->sc_swflags, COM_SW_SOFTCAR)) { + /* XXX perhaps only clear DTR */ + bus_io_write_1(bc, ioh, com_mcr, 0); + } + } + CLR(tp->t_state, TS_BUSY | TS_FLUSH); + if (--comsopen == 0) + untimeout(compoll, NULL); + sc->sc_cua = 0; + splx(s); + ttyclose(tp); +#ifdef COM_DEBUG + /* mark it ready for more use if reattached earlier */ + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT_PENDING)) { + printf("aceclose pending cleared\n"); + } +#endif + CLR(sc->sc_hwflags, COM_HW_ABSENT_PENDING); + +#ifdef notyet /* XXXX */ + if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) { + ttyfree(tp); + sc->sc_tty = 0; + } +#endif + return 0; +} + +int +aceread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + struct com_softc *sc = ace_cd.cd_devs[DEVUNIT(dev)]; + struct tty *tp = sc->sc_tty; + + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + int s = spltty(); + com_absent_notify(sc); + splx(s); + return EIO; + } + + return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); +} + +int +acewrite(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + struct com_softc *sc = ace_cd.cd_devs[DEVUNIT(dev)]; + struct tty *tp = sc->sc_tty; + + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + int s = spltty(); + com_absent_notify(sc); + splx(s); + return EIO; + } + + return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); +} + +struct tty * +acetty(dev) + dev_t dev; +{ + struct com_softc *sc = ace_cd.cd_devs[DEVUNIT(dev)]; + struct tty *tp = sc->sc_tty; + + return (tp); +} + +static u_char +tiocm_xxx2mcr(data) + int data; +{ + u_char m = 0; + + if (ISSET(data, TIOCM_DTR)) + SET(m, MCR_DTR); + if (ISSET(data, TIOCM_RTS)) + SET(m, MCR_RTS); + return m; +} + +int +aceioctl(dev, cmd, data, flag, p) + dev_t dev; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; +{ + int unit = DEVUNIT(dev); + struct com_softc *sc = ace_cd.cd_devs[unit]; + struct tty *tp = sc->sc_tty; + bus_chipset_tag_t bc = sc->sc_bc; + bus_io_handle_t ioh = sc->sc_ioh; + int error; + + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + int s = spltty(); + com_absent_notify(sc); + splx(s); + return EIO; + } + + 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; + + switch (cmd) { + case TIOCSBRK: + SET(sc->sc_lcr, LCR_SBREAK); + bus_io_write_1(bc, ioh, com_lcr, sc->sc_lcr); + break; + case TIOCCBRK: + CLR(sc->sc_lcr, LCR_SBREAK); + bus_io_write_1(bc, ioh, com_lcr, sc->sc_lcr); + break; + case TIOCSDTR: + SET(sc->sc_mcr, sc->sc_dtr); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + break; + case TIOCCDTR: + CLR(sc->sc_mcr, sc->sc_dtr); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + break; + case TIOCMSET: + CLR(sc->sc_mcr, MCR_DTR | MCR_RTS); + case TIOCMBIS: + SET(sc->sc_mcr, tiocm_xxx2mcr(*(int *)data)); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + break; + case TIOCMBIC: + CLR(sc->sc_mcr, tiocm_xxx2mcr(*(int *)data)); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + break; + case TIOCMGET: { + u_char m; + int bits = 0; + + m = sc->sc_mcr; + if (ISSET(m, MCR_DTR)) + SET(bits, TIOCM_DTR); + if (ISSET(m, MCR_RTS)) + SET(bits, TIOCM_RTS); + m = sc->sc_msr; + if (ISSET(m, MSR_DCD)) + SET(bits, TIOCM_CD); + if (ISSET(m, MSR_CTS)) + SET(bits, TIOCM_CTS); + if (ISSET(m, MSR_DSR)) + SET(bits, TIOCM_DSR); + if (ISSET(m, MSR_RI | MSR_TERI)) + SET(bits, TIOCM_RI); + if (bus_io_read_1(bc, ioh, com_ier)) + SET(bits, TIOCM_LE); + *(int *)data = bits; + break; + } + case TIOCGFLAGS: { + int driverbits, userbits = 0; + + driverbits = sc->sc_swflags; + if (ISSET(driverbits, COM_SW_SOFTCAR)) + SET(userbits, TIOCFLAG_SOFTCAR); + if (ISSET(driverbits, COM_SW_CLOCAL)) + SET(userbits, TIOCFLAG_CLOCAL); + if (ISSET(driverbits, COM_SW_CRTSCTS)) + SET(userbits, TIOCFLAG_CRTSCTS); + if (ISSET(driverbits, COM_SW_MDMBUF)) + SET(userbits, TIOCFLAG_MDMBUF); + + *(int *)data = userbits; + break; + } + case TIOCSFLAGS: { + int userbits, driverbits = 0; + + error = suser(p->p_ucred, &p->p_acflag); + if (error != 0) + return(EPERM); + + userbits = *(int *)data; + if (ISSET(userbits, TIOCFLAG_SOFTCAR) || + ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) + SET(driverbits, COM_SW_SOFTCAR); + if (ISSET(userbits, TIOCFLAG_CLOCAL)) + SET(driverbits, COM_SW_CLOCAL); + if (ISSET(userbits, TIOCFLAG_CRTSCTS)) + SET(driverbits, COM_SW_CRTSCTS); + if (ISSET(userbits, TIOCFLAG_MDMBUF)) + SET(driverbits, COM_SW_MDMBUF); + + sc->sc_swflags = driverbits; + break; + } + default: + return ENOTTY; + } + + return 0; +} + +int +comparam(tp, t) + struct tty *tp; + struct termios *t; +{ + struct com_softc *sc = ace_cd.cd_devs[DEVUNIT(tp->t_dev)]; + bus_chipset_tag_t bc = sc->sc_bc; + bus_io_handle_t ioh = sc->sc_ioh; + int ospeed = comspeed(t->c_ospeed); + u_char lcr; + tcflag_t oldcflag; + int s; + + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + int s = spltty(); + com_absent_notify(sc); + splx(s); + return EIO; + } + + /* check requested parameters */ + if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed)) + return EINVAL; + + lcr = ISSET(sc->sc_lcr, LCR_SBREAK); + + switch (ISSET(t->c_cflag, CSIZE)) { + case CS5: + SET(lcr, LCR_5BITS); + break; + case CS6: + SET(lcr, LCR_6BITS); + break; + case CS7: + SET(lcr, LCR_7BITS); + break; + case CS8: + SET(lcr, LCR_8BITS); + break; + } + if (ISSET(t->c_cflag, PARENB)) { + SET(lcr, LCR_PENAB); + if (!ISSET(t->c_cflag, PARODD)) + SET(lcr, LCR_PEVEN); + } + if (ISSET(t->c_cflag, CSTOPB)) + SET(lcr, LCR_STOPB); + + sc->sc_lcr = lcr; + + s = spltty(); + + if (ospeed == 0) { + CLR(sc->sc_mcr, MCR_DTR); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + } + + /* + * Set the FIFO threshold based on the receive speed, if we are + * changing it. + */ + if (sc->sc_initialize || (tp->t_ispeed != t->c_ispeed)) { + sc->sc_initialize = 0; + + if (ospeed != 0) { + /* + * Make sure the transmit FIFO is empty before + * proceeding. If we don't do this, some revisions + * of the UART will hang. Interestingly enough, + * even if we do this will the last character is + * still being pushed out, they don't hang. This + * seems good enough. + */ + while (ISSET(tp->t_state, TS_BUSY)) { + int error; + + ++sc->sc_halt; + error = ttysleep(tp, &tp->t_outq, + TTOPRI | PCATCH, "comprm", 0); + --sc->sc_halt; + if (error) { + splx(s); + comstart(tp); + return (error); + } + } + + bus_io_write_1(bc, ioh, com_lcr, lcr | LCR_DLAB); + bus_io_write_1(bc, ioh, com_dlbl, ospeed); + bus_io_write_1(bc, ioh, com_dlbh, ospeed >> 8); + bus_io_write_1(bc, ioh, com_lcr, lcr); + SET(sc->sc_mcr, MCR_DTR); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + } else + bus_io_write_1(bc, ioh, com_lcr, lcr); + + if (!ISSET(sc->sc_hwflags, COM_HW_HAYESP) && + ISSET(sc->sc_hwflags, COM_HW_FIFO)) + bus_io_write_1(bc, ioh, com_fifo, + FIFO_ENABLE | + (t->c_ispeed <= 1200 ? FIFO_TRIGGER_1 : FIFO_TRIGGER_8)); + } else + bus_io_write_1(bc, ioh, com_lcr, lcr); + + /* When not using CRTSCTS, RTS follows DTR. */ + if (!ISSET(t->c_cflag, CRTSCTS)) { + if (ISSET(sc->sc_mcr, MCR_DTR)) { + if (!ISSET(sc->sc_mcr, MCR_RTS)) { + SET(sc->sc_mcr, MCR_RTS); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + } + } else { + if (ISSET(sc->sc_mcr, MCR_RTS)) { + CLR(sc->sc_mcr, MCR_RTS); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + } + } + sc->sc_dtr = MCR_DTR | MCR_RTS; + } else + sc->sc_dtr = MCR_DTR; + + /* and copy to tty */ + tp->t_ispeed = t->c_ispeed; + tp->t_ospeed = t->c_ospeed; + oldcflag = tp->t_cflag; + tp->t_cflag = t->c_cflag; + + /* + * If DCD is off and MDMBUF is changed, ask the tty layer if we should + * stop the device. + */ + if (!ISSET(sc->sc_msr, MSR_DCD) && + !ISSET(sc->sc_swflags, COM_SW_SOFTCAR) && + ISSET(oldcflag, MDMBUF) != ISSET(tp->t_cflag, MDMBUF) && + (*linesw[tp->t_line].l_modem)(tp, 0) == 0) { + CLR(sc->sc_mcr, sc->sc_dtr); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + } + + /* Just to be sure... */ + splx(s); + comstart(tp); + return 0; +} + +void +comstart_pending(arg) + void *arg; +{ + struct com_softc *sc = arg; + int s; + + s = spltty(); + com_absent_notify(sc); + splx(s); +} + +void +comstart(tp) + struct tty *tp; +{ + struct com_softc *sc = ace_cd.cd_devs[DEVUNIT(tp->t_dev)]; + bus_chipset_tag_t bc = sc->sc_bc; + bus_io_handle_t ioh = sc->sc_ioh; + int s; + + s = spltty(); + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + /* + * not quite good enough: if caller is ttywait() it will + * go to sleep immediately, so hang out a bit and then + * prod caller again. + */ + com_absent_notify(sc); + timeout(comstart_pending, sc, 1); + goto out; + } + if (ISSET(tp->t_state, TS_BUSY)) + goto out; + if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP) || + sc->sc_halt > 0) + goto stopped; + if (ISSET(tp->t_cflag, CRTSCTS) && !ISSET(sc->sc_msr, MSR_CTS)) + goto stopped; + if (tp->t_outq.c_cc <= tp->t_lowat) { + if (ISSET(tp->t_state, TS_ASLEEP)) { + CLR(tp->t_state, TS_ASLEEP); + wakeup(&tp->t_outq); + } + if (tp->t_outq.c_cc == 0) + goto stopped; + selwakeup(&tp->t_wsel); + } + SET(tp->t_state, TS_BUSY); + + if (!ISSET(sc->sc_ier, IER_ETXRDY)) { + SET(sc->sc_ier, IER_ETXRDY); + bus_io_write_1(bc, ioh, com_ier, sc->sc_ier); + } +#ifdef COM_HAYESP + if (ISSET(sc->sc_hwflags, COM_HW_HAYESP)) { + u_char buffer[1024], *cp = buffer; + int n = q_to_b(&tp->t_outq, cp, sizeof buffer); + do + bus_io_write_1(bc, ioh, com_data, *cp++); + while (--n); + } + else +#endif + if (ISSET(sc->sc_hwflags, COM_HW_FIFO)) { + u_char buffer[16], *cp = buffer; + int n = q_to_b(&tp->t_outq, cp, sizeof buffer); + do { + bus_io_write_1(bc, ioh, com_data, *cp++); + } while (--n); + } else + bus_io_write_1(bc, ioh, com_data, getc(&tp->t_outq)); +out: + splx(s); + return; +stopped: + if (ISSET(sc->sc_ier, IER_ETXRDY)) { + CLR(sc->sc_ier, IER_ETXRDY); + bus_io_write_1(bc, ioh, com_ier, sc->sc_ier); + } + splx(s); +} + +/* + * Stop output on a line. + */ +int +acestop(tp, flag) + struct tty *tp; + int flag; +{ + int s; + + s = spltty(); + if (ISSET(tp->t_state, TS_BUSY)) + if (!ISSET(tp->t_state, TS_TTSTOP)) + SET(tp->t_state, TS_FLUSH); + splx(s); + return 0; +} + +void +comdiag(arg) + void *arg; +{ + struct com_softc *sc = arg; + int overflows, floods; + int s; + + s = spltty(); + sc->sc_errors = 0; + overflows = sc->sc_overflows; + sc->sc_overflows = 0; + floods = sc->sc_floods; + sc->sc_floods = 0; + splx(s); + + log(LOG_WARNING, "%s: %d silo overflow%s, %d ibuf overflow%s\n", + sc->sc_dev.dv_xname, + overflows, overflows == 1 ? "" : "s", + floods, floods == 1 ? "" : "s"); +} + +void +compoll(arg) + void *arg; +{ + int unit; + struct com_softc *sc; + struct tty *tp; + register u_char *ibufp; + u_char *ibufend; + register int c; + int s; + static int lsrmap[8] = { + 0, TTY_PE, + TTY_FE, TTY_PE|TTY_FE, + TTY_FE, TTY_PE|TTY_FE, + TTY_FE, TTY_PE|TTY_FE + }; + + s = spltty(); + if (comevents == 0) { + splx(s); + goto out; + } + comevents = 0; + splx(s); + + for (unit = 0; unit < ace_cd.cd_ndevs; unit++) { + sc = ace_cd.cd_devs[unit]; + if (sc == 0 || sc->sc_ibufp == sc->sc_ibuf) + continue; + + tp = sc->sc_tty; + + s = spltty(); + + ibufp = sc->sc_ibuf; + ibufend = sc->sc_ibufp; + + if (ibufp == ibufend) { + splx(s); + continue; + } + + sc->sc_ibufp = sc->sc_ibuf = (ibufp == sc->sc_ibufs[0]) ? + sc->sc_ibufs[1] : sc->sc_ibufs[0]; + sc->sc_ibufhigh = sc->sc_ibuf + COM_IHIGHWATER; + sc->sc_ibufend = sc->sc_ibuf + COM_IBUFSIZE; + + if (tp == 0 || !ISSET(tp->t_state, TS_ISOPEN)) { + splx(s); + continue; + } + + if (ISSET(tp->t_cflag, CRTSCTS) && + !ISSET(sc->sc_mcr, MCR_RTS)) { + /* XXX */ + SET(sc->sc_mcr, MCR_RTS); + bus_io_write_1(sc->sc_bc, sc->sc_ioh, com_mcr, + sc->sc_mcr); + } + + splx(s); + + while (ibufp < ibufend) { + c = *ibufp++; + if (*ibufp & LSR_OE) { + sc->sc_overflows++; + if (sc->sc_errors++ == 0) + timeout(comdiag, sc, 60 * hz); + } + /* This is ugly, but fast. */ + c |= lsrmap[(*ibufp++ & (LSR_BI|LSR_FE|LSR_PE)) >> 2]; + (*linesw[tp->t_line].l_rint)(c, tp); + } + } + +out: + timeout(compoll, NULL, 1); +} + +int +comintr(arg) + void *arg; +{ + struct com_softc *sc = arg; + bus_chipset_tag_t bc = sc->sc_bc; + bus_io_handle_t ioh = sc->sc_ioh; + struct tty *tp; + u_char lsr, data, msr, delta; +#ifdef COM_DEBUG + int n; + struct { + u_char iir, lsr, msr; + } iter[32]; +#endif + + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT) || !sc->sc_tty) + return 0; /* can't do squat. */ + +#ifdef COM_DEBUG + n = 0; + if (ISSET(iter[n].iir = bus_io_read_1(bc, ioh, com_iir), IIR_NOPEND)) + return (0); +#else + if (ISSET(bus_io_read_1(bc, ioh, com_iir), IIR_NOPEND)) + return (0); +#endif + + tp = sc->sc_tty; + + for (;;) { +#ifdef COM_DEBUG + iter[n].lsr = +#endif + lsr = bus_io_read_1(bc, ioh, com_lsr); + + if (ISSET(lsr, LSR_RXRDY)) { + register u_char *p = sc->sc_ibufp; + + comevents = 1; + do { + data = bus_io_read_1(bc, ioh, com_data); + if (ISSET(lsr, LSR_BI)) { +#ifdef notdef + printf("break %02x %02x %02x %02x\n", + sc->sc_msr, sc->sc_mcr, sc->sc_lcr, + sc->sc_dtr); +#endif +#ifdef DDB + if (ISSET(sc->sc_hwflags, + COM_HW_CONSOLE)) { + Debugger(); + goto next; + } +#endif + } + if (p >= sc->sc_ibufend) { + sc->sc_floods++; + if (sc->sc_errors++ == 0) + timeout(comdiag, sc, 60 * hz); + } else { + *p++ = data; + *p++ = lsr; + if (p == sc->sc_ibufhigh && + ISSET(tp->t_cflag, CRTSCTS)) { + /* XXX */ + CLR(sc->sc_mcr, MCR_RTS); + bus_io_write_1(bc, ioh, com_mcr, + sc->sc_mcr); + } + } +#ifdef DDB + next: +#endif +#ifdef COM_DEBUG + if (++n >= 32) + goto ohfudge; + iter[n].lsr = +#endif + lsr = bus_io_read_1(bc, ioh, com_lsr); + } while (ISSET(lsr, LSR_RXRDY)); + + sc->sc_ibufp = p; + } +#ifdef COM_DEBUG + else if (ISSET(lsr, LSR_BI|LSR_FE|LSR_PE|LSR_OE)) + printf("weird lsr %02x\n", lsr); +#endif + +#ifdef COM_DEBUG + iter[n].msr = +#endif + msr = bus_io_read_1(bc, ioh, com_msr); + + if (msr != sc->sc_msr) { + delta = msr ^ sc->sc_msr; + sc->sc_msr = msr; + if (ISSET(delta, MSR_DCD) && + !ISSET(sc->sc_swflags, COM_SW_SOFTCAR) && + (*linesw[tp->t_line].l_modem)(tp, ISSET(msr, MSR_DCD)) == 0) { + CLR(sc->sc_mcr, sc->sc_dtr); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + } + if (ISSET(delta & msr, MSR_CTS) && + ISSET(tp->t_cflag, CRTSCTS)) { + /* the line is up and we want to do rts/cts flow control */ + (*linesw[tp->t_line].l_start)(tp); + } + } + + if (ISSET(lsr, LSR_TXRDY) && ISSET(tp->t_state, TS_BUSY)) { + CLR(tp->t_state, TS_BUSY | TS_FLUSH); + if (sc->sc_halt > 0) + wakeup(&tp->t_outq); + (*linesw[tp->t_line].l_start)(tp); + } + +#ifdef COM_DEBUG + if (++n >= 32) + goto ohfudge; + if (ISSET(iter[n].iir = bus_io_read_1(bc, ioh, com_iir), IIR_NOPEND)) + return (1); +#else + if (ISSET(bus_io_read_1(bc, ioh, com_iir), IIR_NOPEND)) + return (1); +#endif + } +#ifdef COM_DEBUG +ohfudge: + printf("comintr: too many iterations"); + for (n = 0; n < 32; n++) { + if ((n % 4) == 0) + printf("\ncomintr: iter[%02d]", n); + printf(" %02x %02x %02x", iter[n].iir, iter[n].lsr, iter[n].msr); + } + printf("\n"); + printf("comintr: msr %02x mcr %02x lcr %02x ier %02x\n", + sc->sc_msr, sc->sc_mcr, sc->sc_lcr, sc->sc_ier); + printf("comintr: state %08x cc %d\n", sc->sc_tty->t_state, + sc->sc_tty->t_outq.c_cc); +#endif +} + +/* + * Following are all routines needed for COM to act as console + */ +#include <dev/cons.h> + +void +acecnprobe(cp) + struct consdev *cp; +{ + /* XXX NEEDS TO BE FIXED XXX */ + extern int cputype; + bus_chipset_tag_t bc = 0; + bus_io_handle_t ioh; + int found; + + cp->cn_pri = CN_DEAD; + + if (bus_io_map(bc, CONADDR, COM_NPORTS, &ioh)) { + cp->cn_pri = CN_DEAD; + return; + } + ioh = CONADDR; + found = comprobe1(bc, ioh, CONADDR); + if (!found) { + return; + } + + /* locate the major number */ + for (commajor = 0; commajor < nchrdev; commajor++) + if (cdevsw[commajor].d_open == aceopen) + break; + + /* initialize required fields */ + cp->cn_dev = makedev(commajor, CONUNIT); +#ifdef COMCONSOLE + cp->cn_pri = CN_REMOTE; /* Force a serial port console */ +#else + cp->cn_pri = CN_NORMAL; +#endif +} + +void +acecninit(cp) + struct consdev *cp; +{ + +#if 0 + XXX NEEDS TO BE FIXED XXX + comconsbc = ???; +#endif + if (bus_io_map(comconsbc, CONADDR, COM_NPORTS, &comconsioh)) + panic("acecninit: mapping failed"); + + cominit(comconsbc, comconsioh, comdefaultrate); + comconsaddr = CONADDR; + comconsinit = 0; +} + +void +cominit(bc, ioh, rate) + bus_chipset_tag_t bc; + bus_io_handle_t ioh; + int rate; +{ + int s = splhigh(); + u_char stat; + + bus_io_write_1(bc, ioh, com_lcr, LCR_DLAB); + rate = comspeed(comdefaultrate); + bus_io_write_1(bc, ioh, com_dlbl, rate); + bus_io_write_1(bc, ioh, com_dlbh, rate >> 8); + bus_io_write_1(bc, ioh, com_lcr, LCR_8BITS); + bus_io_write_1(bc, ioh, com_ier, IER_ERXRDY | IER_ETXRDY); + bus_io_write_1(bc, ioh, com_fifo, FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_4); + stat = bus_io_read_1(bc, ioh, com_iir); + splx(s); +} + +int +acecngetc(dev) + dev_t dev; +{ + int s = splhigh(); + bus_chipset_tag_t bc = comconsbc; + bus_io_handle_t ioh = comconsioh; + u_char stat, c; + + while (!ISSET(stat = bus_io_read_1(bc, ioh, com_lsr), LSR_RXRDY)) + ; + c = bus_io_read_1(bc, ioh, com_data); + stat = bus_io_read_1(bc, ioh, com_iir); + splx(s); + return c; +} + +/* + * Console kernel output character routine. + */ +void +acecnputc(dev, c) + dev_t dev; + int c; +{ + int s = splhigh(); + bus_chipset_tag_t bc = comconsbc; + bus_io_handle_t ioh = comconsioh; + u_char stat; + register int timo; + +#ifdef KGDB + if (dev != kgdb_dev) +#endif + if (comconsinit == 0) { + cominit(bc, ioh, comdefaultrate); + comconsinit = 1; + } + /* wait for any pending transmission to finish */ + timo = 50000; + while (!ISSET(stat = bus_io_read_1(bc, ioh, com_lsr), LSR_TXRDY) && --timo) + ; + bus_io_write_1(bc, ioh, com_data, c); + /* wait for this transmission to complete */ + timo = 1500000; + while (!ISSET(stat = bus_io_read_1(bc, ioh, com_lsr), LSR_TXRDY) && --timo) + ; + /* clear any interrupts generated by this transmission */ + stat = bus_io_read_1(bc, ioh, com_iir); + splx(s); +} + +void +acecnpollc(dev, on) + dev_t dev; + int on; +{ + +} diff --git a/sys/arch/wgrisc/dev/asc.c b/sys/arch/wgrisc/dev/asc.c new file mode 100644 index 00000000000..13785ab9c0f --- /dev/null +++ b/sys/arch/wgrisc/dev/asc.c @@ -0,0 +1,2096 @@ +/* $OpenBSD: asc.c,v 1.1 1997/02/06 16:02:42 pefo Exp $ */ +/* $NetBSD: asc.c,v 1.10 1994/12/05 19:11:12 dean Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell and Rick Macklem. + * + * 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. + * + * @(#)asc.c 8.3 (Berkeley) 7/3/94 + */ + +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +/* + * HISTORY + * Log: scsi_53C94_hdw.c,v + * Revision 2.5 91/02/05 17:45:07 mrt + * Added author notices + * [91/02/04 11:18:43 mrt] + * + * Changed to use new Mach copyright + * [91/02/02 12:17:20 mrt] + * + * Revision 2.4 91/01/08 15:48:24 rpd + * Added continuation argument to thread_block. + * [90/12/27 rpd] + * + * Revision 2.3 90/12/05 23:34:48 af + * Recovered from pmax merge.. and from the destruction of a disk. + * [90/12/03 23:40:40 af] + * + * Revision 2.1.1.1 90/11/01 03:39:09 af + * Created, from the DEC specs: + * "PMAZ-AA TURBOchannel SCSI Module Functional Specification" + * Workstation Systems Engineering, Palo Alto, CA. Aug 27, 1990. + * And from the NCR data sheets + * "NCR 53C94, 53C95, 53C96 Advances SCSI Controller" + * [90/09/03 af] + */ + +/* + * File: scsi_53C94_hdw.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 9/90 + * + * Bottom layer of the SCSI driver: chip-dependent functions + * + * This file contains the code that is specific to the NCR 53C94 + * SCSI chip (Host Bus Adapter in SCSI parlance): probing, start + * operation, and interrupt routine. + */ + +/* + * This layer works based on small simple 'scripts' that are installed + * at the start of the command and drive the chip to completion. + * The idea comes from the specs of the NCR 53C700 'script' processor. + * + * There are various reasons for this, mainly + * - Performance: identify the common (successful) path, and follow it; + * at interrupt time no code is needed to find the current status + * - Code size: it should be easy to compact common operations + * - Adaptability: the code skeleton should adapt to different chips without + * terrible complications. + * - Error handling: and it is easy to modify the actions performed + * by the scripts to cope with strange but well identified sequences + * + */ + +#include <asc.h> +#if NASC > 0 + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/dkstat.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/conf.h> +#include <sys/errno.h> +#include <sys/device.h> + +#include <vm/vm.h> +#include <vm/vm_kern.h> +#include <vm/vm_page.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#include <machine/cpu.h> +#include <machine/autoconf.h> +#include <machine/pio.h> + +#include <wgrisc/dev/scsi.h> +#include <wgrisc/dev/ascreg.h> +#include <wgrisc/dev/dma.h> + +#include <wgrisc/riscbus/riscbus.h> +#include <wgrisc/wgrisc/wgrisctype.h> + +#define readback(a) { register int foo; foo = (a); } +extern int cputype; + +/* + * In 4ns ticks. + */ +int asc_to_scsi_period[] = { + 32, + 33, + 34, + 35, + 5, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, +}; + +/* + * Internal forward declarations. + */ +static void asc_reset(); +static void asc_startcmd(); + +#ifdef DEBUG +int asc_debug = 1; +int asc_debug_cmd; +int asc_debug_bn; +int asc_debug_sz; +#define NLOG 16 +struct asc_log { + u_int status; + u_char state; + u_char msg; + int target; + int resid; +} asc_log[NLOG], *asc_logp = asc_log; +#define PACK(unit, status, ss, ir) \ + ((unit << 24) | (status << 16) | (ss << 8) | ir) +#endif + +/* + * Scripts are entries in a state machine table. + * A script has four parts: a pre-condition, an action, a command to the chip, + * and an index into asc_scripts for the next state. The first triggers error + * handling if not satisfied and in our case it is formed by the + * values of the interrupt register and status register, this + * basically captures the phase of the bus and the TC and BS + * bits. The action part is just a function pointer, and the + * command is what the 53C94 should be told to do at the end + * of the action processing. This command is only issued and the + * script proceeds if the action routine returns TRUE. + * See asc_intr() for how and where this is all done. + */ +typedef struct script { + int condition; /* expected state at interrupt time */ + int (*action)(); /* extra operations */ + int command; /* command to the chip */ + struct script *next; /* index into asc_scripts for next state */ +} script_t; + +/* Matching on the condition value */ +#define SCRIPT_MATCH(ir, csr) ((ir) | (((csr) & 0x67) << 8)) + +/* forward decls of script actions */ +static int script_nop(); /* when nothing needed */ +static int asc_end(); /* all come to an end */ +static int asc_get_status(); /* get status from target */ +static int asc_dma_in(); /* start reading data from target */ +static int asc_last_dma_in(); /* cleanup after all data is read */ +static int asc_resume_in(); /* resume data in after a message */ +static int asc_resume_dma_in(); /* resume DMA after a disconnect */ +static int asc_dma_out(); /* send data to target via dma */ +static int asc_last_dma_out(); /* cleanup after all data is written */ +static int asc_resume_out(); /* resume data out after a message */ +static int asc_resume_dma_out(); /* resume DMA after a disconnect */ +static int asc_sendsync(); /* negotiate sync xfer */ +static int asc_replysync(); /* negotiate sync xfer */ +static int asc_msg_in(); /* process a message byte */ +static int asc_disconnect(); /* process an expected disconnect */ + +/* Define the index into asc_scripts for various state transitions */ +#define SCRIPT_DATA_IN 0 +#define SCRIPT_CONTINUE_IN 2 +#define SCRIPT_DATA_OUT 3 +#define SCRIPT_CONTINUE_OUT 5 +#define SCRIPT_SIMPLE 6 +#define SCRIPT_GET_STATUS 7 +#define SCRIPT_DONE 8 +#define SCRIPT_MSG_IN 9 +#define SCRIPT_REPLY_SYNC 11 +#define SCRIPT_TRY_SYNC 12 +#define SCRIPT_DISCONNECT 15 +#define SCRIPT_RESEL 16 +#define SCRIPT_RESUME_IN 17 +#define SCRIPT_RESUME_DMA_IN 18 +#define SCRIPT_RESUME_OUT 19 +#define SCRIPT_RESUME_DMA_OUT 20 +#define SCRIPT_RESUME_NO_DATA 21 + +/* + * Scripts + */ +script_t asc_scripts[] = { + /* start data in */ + {SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_DATAI), /* 0 */ + asc_dma_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA, + &asc_scripts[SCRIPT_DATA_IN + 1]}, + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_STATUS), /* 1 */ + asc_last_dma_in, ASC_CMD_I_COMPLETE, + &asc_scripts[SCRIPT_GET_STATUS]}, + + /* continue data in after a chunk is finished */ + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAI), /* 2 */ + asc_dma_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA, + &asc_scripts[SCRIPT_DATA_IN + 1]}, + + /* start data out */ + {SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_DATAO), /* 3 */ + asc_dma_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA, + &asc_scripts[SCRIPT_DATA_OUT + 1]}, + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_STATUS), /* 4 */ + asc_last_dma_out, ASC_CMD_I_COMPLETE, + &asc_scripts[SCRIPT_GET_STATUS]}, + + /* continue data out after a chunk is finished */ + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAO), /* 5 */ + asc_dma_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA, + &asc_scripts[SCRIPT_DATA_OUT + 1]}, + + /* simple command with no data transfer */ + {SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_STATUS), /* 6 */ + script_nop, ASC_CMD_I_COMPLETE, + &asc_scripts[SCRIPT_GET_STATUS]}, + + /* get status and finish command */ + {SCRIPT_MATCH(ASC_INT_FC, ASC_PHASE_MSG_IN), /* 7 */ + asc_get_status, ASC_CMD_MSG_ACPT, + &asc_scripts[SCRIPT_DONE]}, + {SCRIPT_MATCH(ASC_INT_DISC, 0), /* 8 */ + asc_end, ASC_CMD_NOP, + &asc_scripts[SCRIPT_DONE]}, + + /* message in */ + {SCRIPT_MATCH(ASC_INT_FC, ASC_PHASE_MSG_IN), /* 9 */ + asc_msg_in, ASC_CMD_MSG_ACPT, + &asc_scripts[SCRIPT_MSG_IN + 1]}, + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_MSG_IN), /* 10 */ + script_nop, ASC_CMD_XFER_INFO, + &asc_scripts[SCRIPT_MSG_IN]}, + + /* send synchonous negotiation reply */ + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_MSG_OUT), /* 11 */ + asc_replysync, ASC_CMD_XFER_INFO, + &asc_scripts[SCRIPT_REPLY_SYNC]}, + + /* try to negotiate synchonous transfer parameters */ + {SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_MSG_OUT), /* 12 */ + asc_sendsync, ASC_CMD_XFER_INFO, + &asc_scripts[SCRIPT_TRY_SYNC + 1]}, + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_MSG_IN), /* 13 */ + script_nop, ASC_CMD_XFER_INFO, + &asc_scripts[SCRIPT_MSG_IN]}, + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_COMMAND), /* 14 */ + script_nop, ASC_CMD_XFER_INFO | ASC_CMD_DMA, + &asc_scripts[SCRIPT_RESUME_NO_DATA]}, + + /* handle a disconnect */ + {SCRIPT_MATCH(ASC_INT_DISC, ASC_PHASE_DATAO), /* 15 */ + asc_disconnect, ASC_CMD_ENABLE_SEL, + &asc_scripts[SCRIPT_RESEL]}, + + /* reselect sequence: this is just a placeholder so match fails */ + {SCRIPT_MATCH(0, ASC_PHASE_MSG_IN), /* 16 */ + script_nop, ASC_CMD_MSG_ACPT, + &asc_scripts[SCRIPT_RESEL]}, + + /* resume data in after a message */ + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAI), /* 17 */ + asc_resume_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA, + &asc_scripts[SCRIPT_DATA_IN + 1]}, + + /* resume partial DMA data in after a message */ + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAI), /* 18 */ + asc_resume_dma_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA, + &asc_scripts[SCRIPT_DATA_IN + 1]}, + + /* resume data out after a message */ + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAO), /* 19 */ + asc_resume_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA, + &asc_scripts[SCRIPT_DATA_OUT + 1]}, + + /* resume partial DMA data out after a message */ + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAO), /* 20 */ + asc_resume_dma_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA, + &asc_scripts[SCRIPT_DATA_OUT + 1]}, + + /* resume after a message when there is no more data */ + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_STATUS), /* 21 */ + script_nop, ASC_CMD_I_COMPLETE, + &asc_scripts[SCRIPT_GET_STATUS]}, +}; + +/* + * State kept for each active SCSI device. + */ +typedef struct scsi_state { + script_t *script; /* saved script while processing error */ + struct scsi_generic cmd;/* storage for scsi command */ + int statusByte; /* status byte returned during STATUS_PHASE */ + u_int dmaBufSize; /* DMA buffer size */ + int dmalen; /* amount to transfer in this chunk */ + int dmaresid; /* amount not transfered if chunk suspended */ + int cmdlen; /* length of command in cmd */ + int buflen; /* total remaining amount of data to transfer */ + vm_offset_t buf; /* current pointer within scsicmd->buf */ + int flags; /* see below */ + int msglen; /* number of message bytes to read */ + int msgcnt; /* number of message bytes received */ + u_char sync_period; /* DMA synchronous period */ + u_char sync_offset; /* DMA synchronous xfer offset or 0 if async */ + u_char msg_out; /* next MSG_OUT byte to send */ + u_char msg_in[16]; /* buffer for multibyte messages */ +} State; + +/* state flags */ +#define DISCONN 0x001 /* true if currently disconnected from bus */ +#define DMA_IN_PROGRESS 0x002 /* true if data DMA started */ +#define DMA_IN 0x004 /* true if reading from SCSI device */ +#define DMA_OUT 0x010 /* true if writing to SCSI device */ +#define DID_SYNC 0x020 /* true if synchronous offset was negotiated */ +#define TRY_SYNC 0x040 /* true if try neg. synchronous offset */ +#define PARITY_ERR 0x080 /* true if parity error seen */ +#define CHECK_SENSE 0x100 /* true if doing sense command */ + +struct dma_softc_t; +/* + * State kept for each active SCSI host interface (53C94). + */ +struct asc_softc { + struct device sc_dev; /* use as a device */ + asc_regmap_t *regs; /* chip address */ + dma_softc_t dma; /* dma control structure */ + int sc_id; /* SCSI ID of this interface */ + int myidmask; /* ~(1 << myid) */ + int state; /* current SCSI connection state */ + int target; /* target SCSI ID if busy */ + script_t *script; /* next expected interrupt & action */ + struct scsi_xfer *cmdq[ASC_NCMD];/* Pointer to queued commands */ + struct scsi_xfer *cmd[ASC_NCMD];/* Pointer to current active command */ + State st[ASC_NCMD]; /* state info for each active command */ + int min_period; /* Min transfer period clk/byte */ + int max_period; /* Max transfer period clk/byte */ + int ccf; /* CCF, whatever that really is? */ + int timeout_250; /* 250ms timeout */ + int tb_ticks; /* 4ns. ticks/tb channel ticks */ + struct scsi_link sc_link; /* scsi link struct */ +}; + +#define ASC_STATE_IDLE 0 /* idle state */ +#define ASC_STATE_BUSY 1 /* selecting or currently connected */ +#define ASC_STATE_TARGET 2 /* currently selected as target */ +#define ASC_STATE_RESEL 3 /* currently waiting for reselect */ + +typedef struct asc_softc *asc_softc_t; + +static char dma_buffer[MAXPHYS]; /*yieek*/ /*XXX*/ + +/* + * Autoconfiguration data for config. + */ +int ascmatch __P((struct device *, void *, void *)); +void ascattach __P((struct device *, struct device *, void *)); +int ascprint(void *, const char *); + +struct cfattach asc_ca = { + sizeof(struct asc_softc), ascmatch, ascattach +}; +struct cfdriver asc_cd = { + NULL, "asc", DV_DULL, NULL, 0 +}; + +/* + * Glue to the machine dependent scsi + */ +int asc_scsi_cmd __P((struct scsi_xfer *)); +void asc_minphys __P((struct buf *)); + +struct scsi_adapter asc_switch = { + asc_scsi_cmd, + asc_minphys, + NULL, + NULL, +}; + +struct scsi_device asc_dev = { +/*XXX*/ NULL, /* Use default error handler */ +/*XXX*/ NULL, /* have a queue, served by this */ +/*XXX*/ NULL, /* have no async handler */ +/*XXX*/ NULL, /* Use default 'done' routine */ +}; + +static void asc_start(); +static int asc_intr(); + +/* + * Match driver based on name + */ +int +ascmatch(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct cfdata *cf = match; + struct confargs *ca = aux; + + if(!BUS_MATCHNAME(ca, "asc")) + return(0); + return(1); +} + +void +ascattach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + register struct confargs *ca = aux; + register asc_softc_t asc = (void *)self; + register asc_regmap_t *regs; + int id, s, i; + int bufsiz; + + /* + * Initialize hw descriptor, cache some pointers + */ + asc->regs = (asc_regmap_t *)BUS_CVTADDR(ca); + + /* + * Set up machine dependencies. + * 1) how to do dma + * 2) timing based on chip clock frequency + */ + switch (cputype) { + case WGRISC9100: + bufsiz = MAXPHYS; /* Scatter gather in software */ + break; + default: + bufsiz = 64 * 1024; + }; + /* + * Now for timing. WGRISC has a 20Mhz + */ + switch (cputype) { + case WGRISC9100: + asc->min_period = ASC_MIN_PERIOD20; + asc->max_period = ASC_MAX_PERIOD20; + asc->ccf = ASC_CCF(20); + asc->timeout_250 = ASC_TIMEOUT_250(20, asc->ccf); + asc->tb_ticks = 10; + asc->dma.dma_ch = DMA_CH0; + break; + default: + asc->min_period = ASC_MIN_PERIOD12; + asc->max_period = ASC_MAX_PERIOD12; + asc->ccf = ASC_CCF(13); + asc->timeout_250 = ASC_TIMEOUT_250(13, asc->ccf); + asc->tb_ticks = 20; + break; + }; + + asc->state = ASC_STATE_IDLE; + asc->target = -1; + + regs = asc->regs; + + /* + * Reset chip, fully. Note that interrupts are already enabled. + */ + s = splbio(); + + /* preserve our ID for now */ +#if 0 + asc->sc_id = regs->asc_cnfg1 & ASC_CNFG1_MY_BUS_ID; +#else + asc->sc_id = 7; /* XXX Until boot roms set up scsi id */ +#endif + asc->myidmask = ~(1 << asc->sc_id); + + asc_reset(asc, regs); + + /* + * Our SCSI id on the bus. + * The user can set this via the prom on 3maxen/picaen. + * If this changes it is easy to fix: make a default that + * can be changed as boot arg. + */ +#ifdef unneeded + regs->asc_cnfg1 = (regs->asc_cnfg1 & ~ASC_CNFG1_MY_BUS_ID) | + (scsi_initiator_id[unit] & 0x7); + asc->sc_id = regs->asc_cnfg1 & ASC_CNFG1_MY_BUS_ID; +#endif + id = asc->sc_id; + splx(s); + + /* + * Give each target its DMA buffer region. + * The buffer address is the same for all targets, + * the allocated dma viritual scatter/gather space. + */ + for (i = 0; i < ASC_NCMD; i++) { + asc->st[i].dmaBufSize = bufsiz; + } + + /* + * Set up interrupt handler. + */ + BUS_INTR_ESTABLISH(ca, asc_intr, (void *)asc); + + printf(": NCR53C94, target %d\n", id); + + /* + * Fill in the prototype scsi link. + */ + asc->sc_link.adapter_softc = asc; + asc->sc_link.adapter_target = asc->sc_id; + asc->sc_link.adapter = &asc_switch; + asc->sc_link.device = &asc_dev; + asc->sc_link.openings = 2; + + /* + * Now try to attach all the sub devices. + */ + config_found(self, &asc->sc_link, ascprint); +} + +int +ascprint(aux, name) + void *aux; + const char *name; +{ +} + +/* + * Driver breaks down request transfer size. + */ +void +asc_minphys(bp) + struct buf *bp; +{ + minphys(bp); +} + +/* + * Start activity on a SCSI device. + * We maintain information on each device separately since devices can + * connect/disconnect during an operation. + */ +int +asc_scsi_cmd(xs) + struct scsi_xfer *xs; +{ + struct scsi_link *sc_link = xs->sc_link; + struct asc_softc *asc = sc_link->adapter_softc; + State *state = &asc->st[sc_link->target]; + + int flags, s; + + flags = xs->flags; + + /* + * Flush caches for any data buffer + */ + if(xs->datalen != 0) { +#ifdef R4K + R4K_HitFlushDCache(xs->data, xs->datalen); +#else + R3K_FlushDCache(); +#endif + } + /* + * The hack on the next few lines are to avoid buffers + * mapped to UADDR. Realloc to the kva uarea address. + */ + if((u_int)(xs->data) >= UADDR) { + xs->data = ((u_int)(xs->data) & ~UADDR) + (u_char *)(curproc->p_addr); + } + + /* + * Check if another command is already in progress. + * We may have to change this if we allow SCSI devices with + * separate LUNs. + */ + s = splbio(); + if (asc->cmd[sc_link->target]) { + if (asc->cmdq[sc_link->target]) { + splx(s); + printf("asc_scsi_cmd: called when target busy"); + xs->error = XS_DRIVER_STUFFUP; + return TRY_AGAIN_LATER; + } + asc->cmdq[sc_link->target] = xs; + splx(s); + return SUCCESSFULLY_QUEUED; + } + asc->cmd[sc_link->target] = xs; + + /* + * Going to launch. + * Make a local copy of the command and some pointers. + */ + asc_startcmd(asc, sc_link->target); + + /* + * If in startup, interrupts not usable yet. + */ + if(flags & SCSI_POLL) { + return(asc_poll(asc,sc_link->target)); + } + splx(s); + return SUCCESSFULLY_QUEUED; +} + +int +asc_poll(asc, target) + struct asc_softc *asc; + int target; +{ + struct scsi_xfer *scsicmd = asc->cmd[target]; + int count = scsicmd->timeout * 10; + + while(count) { + if(asc->regs->asc_status &ASC_CSR_INT) { + asc_intr(asc); + } + if(scsicmd->flags & ITSDONE) + break; + DELAY(5); + count--; + } + if(count == 0) { + scsicmd->error = XS_TIMEOUT; + asc_end(asc, 0, 0, 0); + } + return COMPLETE; +} + +static void +asc_reset(asc, regs) + asc_softc_t asc; + asc_regmap_t *regs; +{ + int i; + + /* + * Reset chip and SCSI bus. Put everything in a known state. + */ + regs->asc_cmd = ASC_CMD_RESET; + wbflush(); DELAY(25); + regs->asc_cmd = ASC_CMD_NOP; + wbflush(); DELAY(25); + regs->asc_cmd = ASC_CMD_BUS_RESET; + wbflush(); + while(!(regs->asc_status & ASC_CSR_INT)) { + /*void*/ + } + regs->asc_cmd = ASC_CMD_RESET; + wbflush(); DELAY(25); + regs->asc_cmd = ASC_CMD_NOP; + wbflush(); DELAY(25); + + /* + * Set up various chip parameters + */ + regs->asc_ccf = asc->ccf; + wbflush(); DELAY(25); + regs->asc_sel_timo = asc->timeout_250; + /* restore our ID */ + regs->asc_cnfg1 = asc->sc_id | ASC_CNFG1_P_CHECK; + /* include ASC_CNFG2_SCSI2 if you want to allow SCSI II commands */ + regs->asc_cnfg2 = /* ASC_CNFG2_RFB | ASC_CNFG2_SCSI2 | */ ASC_CNFG2_EPL; + regs->asc_cnfg3 = 0; + /* zero anything else */ + ASC_TC_PUT(regs, 0); + regs->asc_syn_p = asc->min_period; + regs->asc_syn_o = 0; /* async for now */ + wbflush(); +} + +/* + * Start a SCSI command on a target. + */ +static void +asc_startcmd(asc, target) + asc_softc_t asc; + int target; +{ + asc_regmap_t *regs; + State *state; + struct scsi_xfer *scsicmd; + int i, len; + + /* + * See if another target is currently selected on this SCSI bus. + */ + if (asc->target >= 0) + return; + + regs = asc->regs; + + /* + * If a reselection is in progress, it is Ok to ignore it since + * the ASC will automatically cancel the command and flush + * the FIFO if the ASC is reselected before the command starts. + * If we try to use ASC_CMD_DISABLE_SEL, we can hang the system if + * a reselect occurs before starting the command. + */ + + asc->state = ASC_STATE_BUSY; + asc->target = target; + + /* cache some pointers */ + scsicmd = asc->cmd[target]; + state = &asc->st[target]; + + /* + * Init the chip and target state. + */ + state->flags = state->flags & (DID_SYNC | CHECK_SENSE); + state->script = (script_t *)0; + state->msg_out = SCSI_NO_OP; + + /* + * Set up for DMA of command output. Also need to flush cache. + */ + if(!(state->flags & CHECK_SENSE)) { + bcopy(scsicmd->cmd, &state->cmd, scsicmd->cmdlen); + state->cmdlen = scsicmd->cmdlen; + state->buf = (vm_offset_t)scsicmd->data; + state->buflen = scsicmd->datalen; + } + len = state->cmdlen; + state->dmalen = len; + +#ifdef DEBUG + if (asc_debug > 1) { + printf("asc_startcmd: %s target %d cmd %x len %d\n", + asc->sc_dev.dv_xname, target, + state->cmd.opcode, state->buflen); + } +#endif + + /* check for simple SCSI command with no data transfer */ + if(state->flags & CHECK_SENSE) { + asc->script = &asc_scripts[SCRIPT_DATA_IN]; + state->flags |= DMA_IN; + } + else if (scsicmd->flags & SCSI_DATA_OUT) { + asc->script = &asc_scripts[SCRIPT_DATA_OUT]; + state->flags |= DMA_OUT; + } + else if (scsicmd->flags & SCSI_DATA_IN) { + asc->script = &asc_scripts[SCRIPT_DATA_IN]; + state->flags |= DMA_IN; + } + else if (state->buflen == 0) { + /* check for sync negotiation */ + if ((scsicmd->flags & /* SCSICMD_USE_SYNC */ 0) && + !(state->flags & DID_SYNC)) { + asc->script = &asc_scripts[SCRIPT_TRY_SYNC]; + state->flags |= TRY_SYNC; + } else + asc->script = &asc_scripts[SCRIPT_SIMPLE]; + state->buf = (vm_offset_t)0; + } + +#ifdef DEBUG + asc_debug_cmd = state->cmd.opcode; + if (state->cmd.opcode == SCSI_READ_EXT) { + asc_debug_bn = (state->cmd.bytes[1] << 24) | + (state->cmd.bytes[2] << 16) | + (state->cmd.bytes[3] << 8) | + state->cmd.bytes[4]; + asc_debug_sz = (state->cmd.bytes[6] << 8) | state->cmd.bytes[7]; + } + asc_logp->status = PACK(asc->sc_dev.dv_unit, 0, 0, asc_debug_cmd); + asc_logp->target = asc->target; + asc_logp->state = asc->script - asc_scripts; + asc_logp->msg = SCSI_DIS_REC_IDENTIFY; + asc_logp->resid = scsicmd->datalen; + if (++asc_logp >= &asc_log[NLOG]) + asc_logp = asc_log; +#endif + + /* preload the FIFO with the message and command to be sent */ + regs->asc_fifo = SCSI_DIS_REC_IDENTIFY | (scsicmd->sc_link->lun & 0x07); + + for( i = 0; i < len; i++ ) { + regs->asc_fifo = ((caddr_t)&state->cmd)[i]; + } + ASC_TC_PUT(regs, 0); + readback(regs->asc_cmd); + regs->asc_cmd = ASC_CMD_DMA; + readback(regs->asc_cmd); + + regs->asc_dbus_id = target; + readback(regs->asc_dbus_id); + regs->asc_syn_p = state->sync_period; + readback(regs->asc_syn_p); + regs->asc_syn_o = state->sync_offset; + readback(regs->asc_syn_o); + +/*XXX PEFO */ +/* we are not using sync transfer now, need to check this if we will */ + + if (state->flags & TRY_SYNC) + regs->asc_cmd = ASC_CMD_SEL_ATN_STOP; + else + regs->asc_cmd = ASC_CMD_SEL_ATN; + readback(regs->asc_cmd); +} + +/* + * Interrupt routine + * Take interrupts from the chip + * + * Implementation: + * Move along the current command's script if + * all is well, invoke error handler if not. + */ +int +asc_intr(sc) + void *sc; +{ + asc_softc_t asc = sc; + asc_regmap_t *regs = asc->regs; + State *state; + script_t *scpt; + int ss, ir, status; + + /* collect ephemeral information */ + status = regs->asc_status; + ss = regs->asc_ss; + + if ((status & ASC_CSR_INT) == 0) /* Make sure it's a real interrupt */ + return; + + ir = regs->asc_intr; /* this resets the previous two */ + scpt = asc->script; + +#ifdef DEBUG + asc_logp->status = PACK(asc->sc_dev.dv_unit, status, ss, ir); + asc_logp->target = (asc->state == ASC_STATE_BUSY) ? asc->target : -1; + asc_logp->state = scpt - asc_scripts; + asc_logp->msg = -1; + asc_logp->resid = 0; + if (++asc_logp >= &asc_log[NLOG]) + asc_logp = asc_log; + if (asc_debug > 2) + printf("asc_intr: status %x ss %x ir %x cond %d:%x\n", + status, ss, ir, scpt - asc_scripts, scpt->condition); +#endif + + /* check the expected state */ + if (SCRIPT_MATCH(ir, status) == scpt->condition) { + /* + * Perform the appropriate operation, then proceed. + */ + if ((*scpt->action)(asc, status, ss, ir)) { + regs->asc_cmd = scpt->command; + readback(regs->asc_cmd); + asc->script = scpt->next; + } + goto done; + } + + /* + * Check for parity error. + * Hardware will automatically set ATN + * to request the device for a MSG_OUT phase. + */ + if (status & ASC_CSR_PE) { + printf("%s: SCSI device %d: incomming parity error seen\n", + asc->sc_dev.dv_xname, asc->target); + asc->st[asc->target].flags |= PARITY_ERR; + } + + /* + * Check for gross error. + * Probably a bug in a device driver. + */ + if (status & ASC_CSR_GE) { + printf("%s: SCSI device %d: gross error\n", + asc->sc_dev.dv_xname, asc->target); + goto abort; + } + + /* check for message in or out */ + if ((ir & ~ASC_INT_FC) == ASC_INT_BS) { + register int len, fifo; + + state = &asc->st[asc->target]; + switch (ASC_PHASE(status)) { + case ASC_PHASE_DATAI: + case ASC_PHASE_DATAO: + ASC_TC_GET(regs, len); + fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT; + printf("asc_intr: data overrun: buflen %d dmalen %d tc %d fifo %d\n", + state->buflen, state->dmalen, len, fifo); + goto abort; + + case ASC_PHASE_MSG_IN: + break; + + case ASC_PHASE_MSG_OUT: + /* + * Check for parity error. + * Hardware will automatically set ATN + * to request the device for a MSG_OUT phase. + */ + if (state->flags & PARITY_ERR) { + state->flags &= ~PARITY_ERR; + state->msg_out = SCSI_MESSAGE_PARITY_ERROR; + /* reset message in counter */ + state->msglen = 0; + } else + state->msg_out = SCSI_NO_OP; + regs->asc_fifo = state->msg_out; + regs->asc_cmd = ASC_CMD_XFER_INFO; + readback(regs->asc_cmd); + goto done; + + case ASC_PHASE_STATUS: + /* probably an error in the SCSI command */ + asc->script = &asc_scripts[SCRIPT_GET_STATUS]; + regs->asc_cmd = ASC_CMD_I_COMPLETE; + readback(regs->asc_cmd); + goto done; + + default: + goto abort; + } + + if (state->script) + goto abort; + + /* + * OK, message coming in clean up whatever is going on. + * Get number of bytes left to transfered from byte counter + * counter decrements when data is trf on the SCSI bus + */ + ASC_TC_GET(regs, len); + fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT; + /* flush any data in the FIFO */ + if (fifo && !(state->flags & DMA_IN_PROGRESS)) { +printf("asc_intr: fifo flush %d len %d fifo %x\n", fifo, len, regs->asc_fifo); + regs->asc_cmd = ASC_CMD_FLUSH; + readback(regs->asc_cmd); + DELAY(2); + } + else if (fifo && state->flags & DMA_IN_PROGRESS) { + if (state->flags & DMA_OUT) { + len += fifo; /* Bytes dma'ed but not sent */ + } + else if (state->flags & DMA_IN) { + u_char *cp; + + printf("asc_intr: IN: dmalen %d len %d fifo %d\n", + state->dmalen, len, fifo); /* XXX */ + } + regs->asc_cmd = ASC_CMD_FLUSH; + readback(regs->asc_cmd); + DELAY(2); + } + if (len && (state->flags & DMA_IN_PROGRESS)) { + /* save number of bytes still to be sent or received */ + state->dmaresid = len; + state->flags &= ~DMA_IN_PROGRESS; + ASC_TC_PUT(regs, 0); +#ifdef DEBUG + if (asc_logp == asc_log) + asc_log[NLOG - 1].resid = len; + else + asc_logp[-1].resid = len; +#endif + /* setup state to resume to */ + if (state->flags & DMA_IN) { + /* + * Since the ASC_CNFG3_SRB bit of the + * cnfg3 register bit is not set, + * we just transferred an extra byte. + * Since we can't resume on an odd byte + * boundary, we copy the valid data out + * and resume DMA at the start address. + */ + if (len & 1) { + printf("asc_intr: msg in len %d (fifo %d)\n", + len, fifo); /* XXX */ + len = state->dmalen - len; + goto do_in; + } + state->script = + &asc_scripts[SCRIPT_RESUME_DMA_IN]; + } else if (state->flags & DMA_OUT) + state->script = + &asc_scripts[SCRIPT_RESUME_DMA_OUT]; + else + state->script = asc->script; + } else if (state->flags & DMA_IN) { + if (len) { +#ifdef DEBUG + printf("asc_intr: 1: bn %d len %d (fifo %d)\n", + asc_debug_bn, len, fifo); /* XXX */ +#endif + goto abort; + } + /* setup state to resume to */ + if (state->flags & DMA_IN_PROGRESS) { + len = state->dmalen; + state->flags &= ~DMA_IN_PROGRESS; + do_in: + DMA_END(&asc->dma); + state->buf += len; + state->buflen -= len; + } + if (state->buflen) + state->script = + &asc_scripts[SCRIPT_RESUME_IN]; + else + state->script = + &asc_scripts[SCRIPT_RESUME_NO_DATA]; + } else if (state->flags & DMA_OUT) { + if (len) { + printf("asc_intr: 2: len %d (fifo %d)\n", len, + fifo); /* XXX */ +/* XXX THEO */ +#if 1 + regs->asc_cmd = ASC_CMD_FLUSH; + readback(regs->asc_cmd); + DELAY(2); + len = 0; +#else + goto abort; +#endif + } + /* + * If this is the last chunk, the next expected + * state is to get status. + */ + if (state->flags & DMA_IN_PROGRESS) { + state->flags &= ~DMA_IN_PROGRESS; + DMA_END(&asc->dma); + len = state->dmalen; + state->buf += len; + state->buflen -= len; + } + if (state->buflen) + state->script = + &asc_scripts[SCRIPT_RESUME_OUT]; + else + state->script = + &asc_scripts[SCRIPT_RESUME_NO_DATA]; + } else if (asc->script == &asc_scripts[SCRIPT_SIMPLE]) + state->script = &asc_scripts[SCRIPT_RESUME_NO_DATA]; + else + state->script = asc->script; + + /* setup to receive a message */ + asc->script = &asc_scripts[SCRIPT_MSG_IN]; + state->msglen = 0; + regs->asc_cmd = ASC_CMD_XFER_INFO; + readback(regs->asc_cmd); + goto done; + } + + /* check for SCSI bus reset */ + if (ir & ASC_INT_RESET) { + register int i; + + printf("%s: SCSI bus reset!!\n", asc->sc_dev.dv_xname); + /* need to flush any pending commands */ + for (i = 0; i < ASC_NCMD; i++) { + if (!asc->cmd[i]) + continue; + asc->cmd[i]->error = XS_DRIVER_STUFFUP; + asc_end(asc, 0, 0, 0); + } + /* rearbitrate synchronous offset */ + for (i = 0; i < ASC_NCMD; i++) { + asc->st[i].sync_offset = 0; + asc->st[i].flags = 0; + } + asc->target = -1; + return; + } + + /* check for command errors */ + if (ir & ASC_INT_ILL) + goto abort; + + /* check for disconnect */ + if (ir & ASC_INT_DISC) { + state = &asc->st[asc->target]; + switch (asc->script - asc_scripts) { + case SCRIPT_DONE: + case SCRIPT_DISCONNECT: + /* + * Disconnects can happen normally when the + * command is complete with the phase being + * either ASC_PHASE_DATAO or ASC_PHASE_MSG_IN. + * The SCRIPT_MATCH() only checks for one phase + * so we can wind up here. + * Perform the appropriate operation, then proceed. + */ + if ((*scpt->action)(asc, status, ss, ir)) { + regs->asc_cmd = scpt->command; + readback(regs->asc_cmd); + asc->script = scpt->next; + } + goto done; + + case SCRIPT_TRY_SYNC: + case SCRIPT_SIMPLE: + case SCRIPT_DATA_IN: + case SCRIPT_DATA_OUT: /* one of the starting scripts */ + if (ASC_SS(ss) == 0) { + /* device did not respond */ + if (regs->asc_flags & ASC_FLAGS_FIFO_CNT) { + regs->asc_cmd = ASC_CMD_FLUSH; + readback(regs->asc_cmd); + } + asc->cmd[asc->target]->error = XS_DRIVER_STUFFUP; + asc_end(asc, status, ss, ir); + return; + } + /* FALLTHROUGH */ + + default: + printf("%s: SCSI device %d: unexpected disconnect\n", + asc->sc_dev.dv_xname, asc->target); +#ifdef DEBUG + asc_DumpLog("asc_disc"); +#endif + /* + * On rare occasions my RZ24 does a disconnect during + * data in phase and the following seems to keep it + * happy. + * XXX Should a scsi disk ever do this?? + */ + asc->script = &asc_scripts[SCRIPT_RESEL]; + asc->state = ASC_STATE_RESEL; + state->flags |= DISCONN; + regs->asc_cmd = ASC_CMD_ENABLE_SEL; + readback(regs->asc_cmd); + return; + } + } + + /* check for reselect */ + if (ir & ASC_INT_RESEL) { + unsigned fifo, id, msg; + + fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT; + if (fifo < 2) + goto abort; + /* read unencoded SCSI ID and convert to binary */ + msg = regs->asc_fifo & asc->myidmask; + for (id = 0; (msg & 1) == 0; id++) + msg >>= 1; + /* read identify message */ + msg = regs->asc_fifo; +#ifdef DEBUG + if (asc_logp == asc_log) + asc_log[NLOG - 1].msg = msg; + else + asc_logp[-1].msg = msg; +#endif + asc->state = ASC_STATE_BUSY; + asc->target = id; + state = &asc->st[id]; + asc->script = state->script; + state->script = (script_t *)0; + if (!(state->flags & DISCONN)) + goto abort; + state->flags &= ~DISCONN; + regs->asc_syn_p = state->sync_period; + regs->asc_syn_o = state->sync_offset; + regs->asc_cmd = ASC_CMD_MSG_ACPT; + readback(regs->asc_cmd); + goto done; + } + + /* check if we are being selected as a target */ + if (ir & (ASC_INT_SEL | ASC_INT_SEL_ATN)) + goto abort; + + /* + * 'ir' must be just ASC_INT_FC. + * This is normal if canceling an ASC_ENABLE_SEL. + */ + +done: + wbflush(); + /* + * If the next interrupt comes in immediatly the interrupt + * dispatcher (which we are returning to) will catch it + * before returning to the interrupted code. + */ + return; + +abort: +#ifdef DEBUG + asc_DumpLog("asc_intr"); +#endif +#if 0 + panic("asc_intr"); +#else + boot(4); /* XXX */ +#endif +} + +/* + * All the many little things that the interrupt + * routine might switch to. + */ + +/* ARGSUSED */ +static int +script_nop(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + return (1); +} + +/* ARGSUSED */ +static int +asc_get_status(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register int data; + + /* + * Get the last two bytes in the FIFO. + */ + if ((data = regs->asc_flags & ASC_FLAGS_FIFO_CNT) != 2) { + printf("asc_get_status: cmdreg %x, fifo cnt %d\n", + regs->asc_cmd, data); /* XXX */ +#ifdef DEBUG + asc_DumpLog("get_status"); /* XXX */ +#endif + if (data < 2) { + asc->regs->asc_cmd = ASC_CMD_MSG_ACPT; + readback(asc->regs->asc_cmd); + return (0); + } + do { + data = regs->asc_fifo; + } while ((regs->asc_flags & ASC_FLAGS_FIFO_CNT) > 2); + } + + /* save the status byte */ + asc->st[asc->target].statusByte = data = regs->asc_fifo; +#ifdef DEBUG + if (asc_logp == asc_log) + asc_log[NLOG - 1].msg = data; + else + asc_logp[-1].msg = data; +#endif + + /* get the (presumed) command_complete message */ + if ((data = regs->asc_fifo) == SCSI_COMMAND_COMPLETE) + return (1); + +#ifdef DEBUG + printf("asc_get_status: status %x cmd %x\n", + asc->st[asc->target].statusByte, data); + asc_DumpLog("asc_get_status"); +#endif + return (0); +} + +/* ARGSUSED */ +static int +asc_end(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + struct scsi_xfer *scsicmd; + struct scsi_link *sc_link; + State *state; + int i, target; + + asc->state = ASC_STATE_IDLE; + target = asc->target; + asc->target = -1; + scsicmd = asc->cmd[target]; + sc_link = scsicmd->sc_link; + asc->cmd[target] = (struct scsi_xfer *)0; + state = &asc->st[target]; + +#ifdef DEBUG + if (asc_debug > 1) { + printf("asc_end: %s target %d cmd %x err %d resid %d\n", + asc->sc_dev.dv_xname, target, + state->cmd.opcode, scsicmd->error, state->buflen); + } +#endif +#ifdef DIAGNOSTIC + if (target < 0 || !scsicmd) + panic("asc_end"); +#endif + + /* look for disconnected devices */ + for (i = 0; i < ASC_NCMD; i++) { + if (!asc->cmd[i] || !(asc->st[i].flags & DISCONN)) + continue; + asc->regs->asc_cmd = ASC_CMD_ENABLE_SEL; + readback(asc->regs->asc_cmd); + asc->state = ASC_STATE_RESEL; + asc->script = &asc_scripts[SCRIPT_RESEL]; + break; + } + + if(scsicmd->error == XS_NOERROR && !(state->flags & CHECK_SENSE)) { + if((state->statusByte & ST_MASK) == SCSI_CHECK) { + struct scsi_sense *ss = (void *)&state->cmd; + /* Save return values */ + scsicmd->resid = state->buflen; + scsicmd->status = state->statusByte; + /* Set up sense request command */ + bzero(ss, sizeof(*ss)); + ss->opcode = REQUEST_SENSE; + ss->byte2 = sc_link->lun << 5; + ss->length = sizeof(struct scsi_sense_data); + state->cmdlen = sizeof(*ss); + state->buf = (vm_offset_t)&scsicmd->sense; + state->buflen = sizeof(struct scsi_sense_data); + state->flags |= CHECK_SENSE; +#ifdef R4K + R4K_HitFlushDCache(state->buf, state->buflen); +#else + R3K_FlushDCache(); +#endif + asc->cmd[target] = scsicmd; + asc_startcmd(asc, target); + return(0); + } + } + + if(scsicmd->error == XS_NOERROR && (state->flags & CHECK_SENSE)) { + scsicmd->error = XS_SENSE; + } + else { + scsicmd->resid = state->buflen; + } + state->flags &= ~CHECK_SENSE; + + /* + * Look for another device that is ready. + * May want to keep last one started and increment for fairness + * rather than always starting at zero. + */ + for (i = 0; i < ASC_NCMD; i++) { + if (asc->cmd[i] == 0 && asc->cmdq[i] != 0) { + asc->cmd[i] = asc->cmdq[i]; + asc->cmdq[i] = 0; + } + } + for (i = 0; i < ASC_NCMD; i++) { + /* don't restart a disconnected command */ + if (!asc->cmd[i] || (asc->st[i].flags & DISCONN)) + continue; + asc_startcmd(asc, i); + break; + } + + /* signal device driver that the command is done */ + scsicmd->flags |= ITSDONE; + scsi_done(scsicmd); + + return (0); +} + +/* ARGSUSED */ +static int +asc_dma_in(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + register int len; + + /* check for previous chunk in buffer */ + if (state->flags & DMA_IN_PROGRESS) { + /* + * Only count bytes that have been copied to memory. + * There may be some bytes in the FIFO if synchonous transfers + * are in progress. + */ + DMA_END(&asc->dma); + ASC_TC_GET(regs, len); + len = state->dmalen - len; + state->buf += len; + state->buflen -= len; + } + + /* setup to start reading the next chunk */ + len = state->buflen; +#ifdef DEBUG + if (asc_logp == asc_log) + asc_log[NLOG - 1].resid = len; + else + asc_logp[-1].resid = len; +#endif + if (len > state->dmaBufSize) + len = state->dmaBufSize; + state->dmalen = len; + DMA_START(&asc->dma, (caddr_t)state->buf, len, DMA_FROM_DEV); + ASC_TC_PUT(regs, len); +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_dma_in: buflen %d, len %d\n", state->buflen, len); +#endif + + /* check for next chunk */ + state->flags |= DMA_IN_PROGRESS; + if (len != state->buflen) { + regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA; + readback(regs->asc_cmd); + asc->script = &asc_scripts[SCRIPT_CONTINUE_IN]; + return (0); + } + return (1); +} + +/* ARGSUSED */ +static int +asc_last_dma_in(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + register int len, fifo; + + DMA_END(&asc->dma); + ASC_TC_GET(regs, len); + fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT; +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_last_dma_in: buflen %d dmalen %d tc %d fifo %d\n", + state->buflen, state->dmalen, len, fifo); +#endif + if (fifo) { + /* device must be trying to send more than we expect */ + regs->asc_cmd = ASC_CMD_FLUSH; + readback(regs->asc_cmd); + } + state->flags &= ~DMA_IN_PROGRESS; + len = state->dmalen - len; + state->buflen -= len; + + return (1); +} + +/* ARGSUSED */ +static int +asc_resume_in(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + register int len; + + /* setup to start reading the next chunk */ + len = state->buflen; +#ifdef DEBUG + if (asc_logp == asc_log) + asc_log[NLOG - 1].resid = len; + else + asc_logp[-1].resid = len; +#endif + if (len > state->dmaBufSize) + len = state->dmaBufSize; + state->dmalen = len; +#ifdef DEBUG + if (asc_logp == asc_log) + asc_log[NLOG - 1].resid = len; + else + asc_logp[-1].resid = len; +#endif + DMA_START(&asc->dma, (caddr_t)state->buf, len, DMA_FROM_DEV); + ASC_TC_PUT(regs, len); +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_resume_in: buflen %d, len %d\n", state->buflen, + len); +#endif + + /* check for next chunk */ + state->flags |= DMA_IN_PROGRESS; + if (len != state->buflen) { + regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA; + readback(regs->asc_cmd); + asc->script = &asc_scripts[SCRIPT_CONTINUE_IN]; + return (0); + } + return (1); +} + +/* ARGSUSED */ +static int +asc_resume_dma_in(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + register int len, off; + + /* setup to finish reading the current chunk */ + len = state->dmaresid; + off = state->dmalen - len; + if ((off & 1) && state->sync_offset) { + printf("asc_resume_dma_in: odd xfer dmalen %d len %d off %d\n", + state->dmalen, len, off); /* XXX */ + regs->asc_res_fifo = ((u_char *)state->buf)[off]; +/*XXX Need to flush cache ? */ + } + DMA_START(&asc->dma, (caddr_t)state->buf + off, len, DMA_FROM_DEV); + ASC_TC_PUT(regs, len); +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_resume_dma_in: buflen %d dmalen %d len %d off %d\n", + state->dmalen, state->buflen, len, off); +#endif + + /* check for next chunk */ + state->flags |= DMA_IN_PROGRESS; + if (state->dmalen != state->buflen) { + regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA; + readback(regs->asc_cmd); + asc->script = &asc_scripts[SCRIPT_CONTINUE_IN]; + return (0); + } + return (1); +} + +/* ARGSUSED */ +static int +asc_dma_out(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + register int len, fifo; + + if (state->flags & DMA_IN_PROGRESS) { + /* check to be sure previous chunk was finished */ + ASC_TC_GET(regs, len); + fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT; + if (len || fifo) + printf("asc_dma_out: buflen %d dmalen %d tc %d fifo %d\n", + state->buflen, state->dmalen, len, fifo); /* XXX */ + len += fifo; + len = state->dmalen - len; + state->buf += len; + state->buflen -= len; + } + + /* setup for this chunk */ + len = state->buflen; +#ifdef DEBUG + if (asc_logp == asc_log) + asc_log[NLOG - 1].resid = len; + else + asc_logp[-1].resid = len; +#endif + if (len > state->dmaBufSize) + len = state->dmaBufSize; + state->dmalen = len; + DMA_START(&asc->dma, (caddr_t)state->buf, len, DMA_TO_DEV); + ASC_TC_PUT(regs, len); +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_dma_out: buflen %d, len %d\n", state->buflen, len); +#endif + + /* check for next chunk */ + state->flags |= DMA_IN_PROGRESS; + if (len != state->buflen) { + regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA; + readback(regs->asc_cmd); + asc->script = &asc_scripts[SCRIPT_CONTINUE_OUT]; + return (0); + } + return (1); +} + +/* ARGSUSED */ +static int +asc_last_dma_out(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + register int len, fifo; + + ASC_TC_GET(regs, len); + fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT; +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_last_dma_out: buflen %d dmalen %d tc %d fifo %d\n", + state->buflen, state->dmalen, len, fifo); +#endif + if (fifo) { + len += fifo; + regs->asc_cmd = ASC_CMD_FLUSH; + readback(regs->asc_cmd); + } + state->flags &= ~DMA_IN_PROGRESS; + len = state->dmalen - len; + state->buflen -= len; + return (1); +} + +/* ARGSUSED */ +static int +asc_resume_out(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + register int len; + + /* setup for this chunk */ + len = state->buflen; +#ifdef DEBUG + if (asc_logp == asc_log) + asc_log[NLOG - 1].resid = len; + else + asc_logp[-1].resid = len; +#endif + if (len > state->dmaBufSize) + len = state->dmaBufSize; + state->dmalen = len; +#ifdef DEBUG + if (asc_logp == asc_log) + asc_log[NLOG - 1].resid = len; + else + asc_logp[-1].resid = len; +#endif + DMA_START(&asc->dma, (caddr_t)state->buf, len, DMA_TO_DEV); + ASC_TC_PUT(regs, len); +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_resume_out: buflen %d, len %d\n", state->buflen, + len); +#endif + + /* check for next chunk */ + state->flags |= DMA_IN_PROGRESS; + if (len != state->buflen) { + regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA; + readback(regs->asc_cmd); + asc->script = &asc_scripts[SCRIPT_CONTINUE_OUT]; + return (0); + } + return (1); +} + +/* ARGSUSED */ +static int +asc_resume_dma_out(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + register int len, off; + + /* setup to finish writing this chunk */ + len = state->dmaresid; + off = state->dmalen - len; + if (off & 1) { + printf("asc_resume_dma_out: odd xfer dmalen %d len %d off %d\n", + state->dmalen, len, off); /* XXX */ + regs->asc_fifo = ((u_char *)state->buf)[off]; +/*XXX Need to flush Cache ? */ + off++; + len--; + } + DMA_START(&asc->dma, (caddr_t)state->buf + off, len, DMA_TO_DEV); + ASC_TC_PUT(regs, len); +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_resume_dma_out: buflen %d dmalen %d len %d off %d\n", + state->dmalen, state->buflen, len, off); +#endif + + /* check for next chunk */ + state->flags |= DMA_IN_PROGRESS; + if (state->dmalen != state->buflen) { + regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA; + readback(regs->asc_cmd); + asc->script = &asc_scripts[SCRIPT_CONTINUE_OUT]; + return (0); + } + return (1); +} + +/* ARGSUSED */ +static int +asc_sendsync(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + + /* send the extended synchronous negotiation message */ + regs->asc_fifo = SCSI_EXTENDED_MSG; + wbflush(); + regs->asc_fifo = 3; + wbflush(); + regs->asc_fifo = SCSI_SYNCHRONOUS_XFER; + wbflush(); + regs->asc_fifo = SCSI_MIN_PERIOD; + wbflush(); + regs->asc_fifo = ASC_MAX_OFFSET; + /* state to resume after we see the sync reply message */ + state->script = asc->script + 2; + state->msglen = 0; + return (1); +} + +/* ARGSUSED */ +static int +asc_replysync(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_replysync: %x %x\n", + asc_to_scsi_period[state->sync_period] * asc->tb_ticks, + state->sync_offset); +#endif + /* send synchronous transfer in response to a request */ + regs->asc_fifo = SCSI_EXTENDED_MSG; + wbflush(); + regs->asc_fifo = 3; + wbflush(); + regs->asc_fifo = SCSI_SYNCHRONOUS_XFER; + wbflush(); + regs->asc_fifo = asc_to_scsi_period[state->sync_period] * asc->tb_ticks; + wbflush(); + regs->asc_fifo = state->sync_offset; + regs->asc_cmd = ASC_CMD_XFER_INFO; + readback(regs->asc_cmd); + + /* return to the appropriate script */ + if (!state->script) { +#ifdef DEBUG + asc_DumpLog("asc_replsync"); +#endif + panic("asc_replysync"); + } + asc->script = state->script; + state->script = (script_t *)0; + return (0); +} + +/* ARGSUSED */ +static int +asc_msg_in(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + register int msg; + int i; + + /* read one message byte */ + msg = regs->asc_fifo; +#ifdef DEBUG + if (asc_logp == asc_log) + asc_log[NLOG - 1].msg = msg; + else + asc_logp[-1].msg = msg; +#endif + + /* check for multi-byte message */ + if (state->msglen != 0) { + /* first byte is the message length */ + if (state->msglen < 0) { + state->msglen = msg; + return (1); + } + if (state->msgcnt >= state->msglen) + goto abort; + state->msg_in[state->msgcnt++] = msg; + + /* did we just read the last byte of the message? */ + if (state->msgcnt != state->msglen) + return (1); + + /* process an extended message */ +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_msg_in: msg %x %x %x\n", + state->msg_in[0], + state->msg_in[1], + state->msg_in[2]); +#endif + switch (state->msg_in[0]) { + case SCSI_SYNCHRONOUS_XFER: + state->flags |= DID_SYNC; + state->sync_offset = state->msg_in[2]; + + /* convert SCSI period to ASC period */ + i = state->msg_in[1] / asc->tb_ticks; + if (i < asc->min_period) + i = asc->min_period; + else if (i >= asc->max_period) { + /* can't do sync transfer, period too long */ + printf("%s: SCSI device %d: sync xfer period too long (%d)\n", + asc->sc_dev.dv_xname, asc->target, i); + i = asc->max_period; + state->sync_offset = 0; + } + if ((i * asc->tb_ticks) != state->msg_in[1]) + i++; + state->sync_period = i & 0x1F; + + /* + * If this is a request, check minimums and + * send back an acknowledge. + */ + if (!(state->flags & TRY_SYNC)) { + regs->asc_cmd = ASC_CMD_SET_ATN; + readback(regs->asc_cmd); + + if (state->sync_period < asc->min_period) + state->sync_period = + asc->min_period; + if (state->sync_offset > ASC_MAX_OFFSET) + state->sync_offset = + ASC_MAX_OFFSET; + asc->script = &asc_scripts[SCRIPT_REPLY_SYNC]; + regs->asc_syn_p = state->sync_period; + readback(regs->asc_syn_p); + regs->asc_syn_o = state->sync_offset; + readback(regs->asc_syn_o); + regs->asc_cmd = ASC_CMD_MSG_ACPT; + readback(regs->asc_cmd); + return (0); + } + + regs->asc_syn_p = state->sync_period; + readback(regs->asc_syn_p); + regs->asc_syn_o = state->sync_offset; + readback(regs->asc_syn_o); + goto done; + + default: + printf("%s: SCSI device %d: rejecting extended message 0x%x\n", + asc->sc_dev.dv_xname, asc->target, + state->msg_in[0]); + goto reject; + } + } + + /* process first byte of a message */ +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_msg_in: msg %x\n", msg); +#endif + switch (msg) { +#if 0 + case SCSI_MESSAGE_REJECT: + printf(" did not like SYNCH xfer "); /* XXX */ + state->flags |= DID_SYNC; + regs->asc_cmd = ASC_CMD_MSG_ACPT; + readback(regs->asc_cmd); + status = asc_wait(regs, ASC_CSR_INT); + ir = regs->asc_intr; + /* some just break out here, some dont */ + if (ASC_PHASE(status) == ASC_PHASE_MSG_OUT) { + regs->asc_fifo = SCSI_ABORT; + regs->asc_cmd = ASC_CMD_XFER_INFO; + readback(regs->asc_cmd); + status = asc_wait(regs, ASC_CSR_INT); + ir = regs->asc_intr; + } + if (ir & ASC_INT_DISC) { + asc_end(asc, status, 0, ir); + return (0); + } + goto status; +#endif /* 0 */ + + case SCSI_EXTENDED_MSG: /* read an extended message */ + /* setup to read message length next */ + state->msglen = -1; + state->msgcnt = 0; + return (1); + + case SCSI_NO_OP: + break; + + case SCSI_SAVE_DATA_POINTER: + /* expect another message */ + return (1); + + case SCSI_RESTORE_POINTERS: + /* + * Need to do the following if resuming synchonous data in + * on an odd byte boundary. + regs->asc_cnfg2 |= ASC_CNFG2_RFB; + */ + break; + + case SCSI_DISCONNECT: + if (state->flags & DISCONN) + goto abort; + state->flags |= DISCONN; + regs->asc_cmd = ASC_CMD_MSG_ACPT; + readback(regs->asc_cmd); + asc->script = &asc_scripts[SCRIPT_DISCONNECT]; + return (0); + + default: + printf("%s: SCSI device %d: rejecting message 0x%x\n", + asc->sc_dev.dv_xname, asc->target, msg); + reject: + /* request a message out before acknowledging this message */ + state->msg_out = SCSI_MESSAGE_REJECT; + regs->asc_cmd = ASC_CMD_SET_ATN; + readback(regs->asc_cmd); + } + +done: + /* return to original script */ + regs->asc_cmd = ASC_CMD_MSG_ACPT; + readback(regs->asc_cmd); + if (!state->script) { + abort: +#ifdef DEBUG + asc_DumpLog("asc_msg_in"); +#endif + panic("asc_msg_in"); + } + asc->script = state->script; + state->script = (script_t *)0; + return (0); +} + +/* ARGSUSED */ +static int +asc_disconnect(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register State *state = &asc->st[asc->target]; + +#ifdef DIAGNOSTIC + if (!(state->flags & DISCONN)) { + printf("asc_disconnect: device %d: DISCONN not set!\n", + asc->target); + } +#endif /* DIAGNOSTIC */ + asc->target = -1; + asc->state = ASC_STATE_RESEL; + return (1); +} + +#ifdef DEBUG +/* + * Dump the log buffer. + */ +asc_DumpLog(str) + char *str; +{ + register struct asc_log *lp; + register u_int status; + + printf("asc: %s: cmd %x bn %d cnt %d\n", str, asc_debug_cmd, + asc_debug_bn, asc_debug_sz); + lp = asc_logp; + do { + status = lp->status; + printf("asc%d tgt %d status %x ss %x ir %x cond %d:%x msg %x resid %d\n", + status >> 24, + lp->target, + (status >> 16) & 0xFF, + (status >> 8) & 0xFF, + status & 0XFF, + lp->state, + asc_scripts[lp->state].condition, + lp->msg, lp->resid); + if (++lp >= &asc_log[NLOG]) + lp = asc_log; + } while (lp != asc_logp); +} +#endif /* DEBUG */ + +#endif /* NASC > 0 */ diff --git a/sys/arch/wgrisc/dev/ascreg.h b/sys/arch/wgrisc/dev/ascreg.h new file mode 100644 index 00000000000..3a5a213ba36 --- /dev/null +++ b/sys/arch/wgrisc/dev/ascreg.h @@ -0,0 +1,326 @@ +/* $OpenBSD: ascreg.h,v 1.1 1997/02/06 16:02:42 pefo Exp $ */ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell and Rick Macklem. + * + * 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. + * + * from: @(#)ascreg.h 8.1 (Berkeley) 6/10/93 + * $Id: ascreg.h,v 1.1 1997/02/06 16:02:42 pefo Exp $ + */ + +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +/* + * HISTORY + * Log: scsi_53C94.h,v + * Revision 2.4 91/02/05 17:44:59 mrt + * Added author notices + * [91/02/04 11:18:32 mrt] + * + * Changed to use new Mach copyright + * [91/02/02 12:17:11 mrt] + * + * Revision 2.3 90/12/05 23:34:46 af + * Documented max DMA xfer size. + * [90/12/03 23:39:36 af] + * + * Revision 2.1.1.1 90/11/01 03:38:54 af + * Created, from the DEC specs: + * "PMAZ-AA TURBOchannel SCSI Module Functional Specification" + * Workstation Systems Engineering, Palo Alto, CA. Aug 27, 1990. + * And from the NCR data sheets + * "NCR 53C94, 53C95, 53C96 Advanced SCSI Controller" + * [90/09/03 af] + */ + +/* + * File: scsi_53C94.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 9/90 + * + * Defines for the NCR 53C94 ASC (SCSI interface) + * Some gotcha came from the "86C01/53C94 DMA lab work" written + * by Ken Stewart (NCR MED Logic Products Applications Engineer) + * courtesy of NCR. Thanks Ken ! + */ + +#define ASC_OFFSET_53C94 0x0 /* from module base */ + +#define ASC_NCMD 7 /* Number of simultaneous cmds */ + +/* + * Synch xfer parameters, and timing conversions + */ +#define SCSI_MIN_PERIOD 50 /* in 4 nsecs units */ +#define ASC_MIN_PERIOD40 8 /* in CLKS/BYTE, 1 CLK = 25nsecs */ +#define ASC_MIN_PERIOD25 5 /* in CLKS/BYTE, 1 CLK = 40nsecs */ +#define ASC_MIN_PERIOD20 4 /* in CLKS/BYTE, 1 CLK = 50nsecs */ +#define ASC_MIN_PERIOD12 3 /* in CLKS/BYTE, 1 CLK = 80nsecs */ +#define ASC_MAX_PERIOD40 56 /* in CLKS/BYTE, 1 CLK = 25nsecs */ +#define ASC_MAX_PERIOD25 35 /* in CLKS/BYTE, 1 CLK = 40nsecs */ +#define ASC_MAX_PERIOD20 28 /* in CLKS/BYTE, 1 CLK = 50nsecs */ +#define ASC_MAX_PERIOD12 18 /* in CLKS/BYTE, 1 CLK = 80nsecs */ +#define ASC_MAX_OFFSET 15 /* pure number */ +/* + * Register map, padded as needed + */ + +typedef volatile struct { + u_char asc_tc_lsb; /* 00 rw: Transfer Counter LSB */ + u_char asc_tc_msb; /* 01 rw: Transfer Counter MSB */ + u_char asc_fifo; /* 02 rw: FIFO top */ + u_char asc_cmd; /* 03 rw: Command */ + u_char asc_status; /* 04 r: Status */ +#define asc_dbus_id asc_status /* 04 w: Destination Bus ID */ + u_char asc_intr; /* 05 r: Interrupt */ +#define asc_sel_timo asc_intr /* 05 w: (re)select timeout */ + u_char asc_ss; /* 06 r: Sequence Step */ +#define asc_syn_p asc_ss /* 06 w: synchronous period */ + u_char asc_flags; /* 07 r: FIFO flags + seq step */ +#define asc_syn_o asc_flags /* 07 w: synchronous offset */ + u_char asc_cnfg1; /* 08 rw: Configuration 1 */ + u_char asc_ccf; /* 09 w: Clock Conv. Factor */ + u_char asc_test; /* 0a w: Test Mode */ + u_char asc_cnfg2; /* 0b rw: Configuration 2 */ + u_char asc_cnfg3; /* 0c rw: Configuration 3 */ + u_char asc_res_fifo; /* 0d w: Reserve FIFO byte */ + u_char asc_tc_xsb; /* 0e rw: Transfer Counter Xtended */ +} asc_regmap_t; + +/* + * Transfer Count: access macros + * That a NOP is required after loading the dma counter + * I learned on the NCR test code. Sic. + */ + +#define ASC_TC_MAX 0x10000 + +#define ASC_TC_GET(ptr, val) \ + val = (ptr)->asc_tc_lsb | ((ptr)->asc_tc_msb << 8) | ((ptr)->asc_tc_xsb << 16) +#define ASC_TC_PUT(ptr, val) \ + (ptr)->asc_tc_lsb = (val); \ + (ptr)->asc_tc_msb = (val) >> 8; \ + (ptr)->asc_tc_xsb = (val) >> 16; \ + (ptr)->asc_cmd = ASC_CMD_NOP | ASC_CMD_DMA; + +/* + * Command register (command codes) + */ + +#define ASC_CMD_DMA 0x80 + /* Miscellaneous */ +#define ASC_CMD_NOP 0x00 +#define ASC_CMD_FLUSH 0x01 +#define ASC_CMD_RESET 0x02 +#define ASC_CMD_BUS_RESET 0x03 + /* Initiator state */ +#define ASC_CMD_XFER_INFO 0x10 +#define ASC_CMD_I_COMPLETE 0x11 +#define ASC_CMD_MSG_ACPT 0x12 +#define ASC_CMD_XFER_PAD 0x18 +#define ASC_CMD_SET_ATN 0x1a +#define ASC_CMD_CLR_ATN 0x1b + /* Target state */ +#define ASC_CMD_SND_MSG 0x20 +#define ASC_CMD_SND_STATUS 0x21 +#define ASC_CMD_SND_DATA 0x22 +#define ASC_CMD_DISC_SEQ 0x23 +#define ASC_CMD_TERM 0x24 +#define ASC_CMD_T_COMPLETE 0x25 +#define ASC_CMD_DISC 0x27 +#define ASC_CMD_RCV_MSG 0x28 +#define ASC_CMD_RCV_CDB 0x29 +#define ASC_CMD_RCV_DATA 0x2a +#define ASC_CMD_RCV_CMD 0x2b +#define ASC_CMD_ABRT_DMA 0x04 + /* Disconnected state */ +#define ASC_CMD_RESELECT 0x40 +#define ASC_CMD_SEL 0x41 +#define ASC_CMD_SEL_ATN 0x42 +#define ASC_CMD_SEL_ATN_STOP 0x43 +#define ASC_CMD_ENABLE_SEL 0x44 +#define ASC_CMD_DISABLE_SEL 0x45 +#define ASC_CMD_SEL_ATN3 0x46 + +/* + * Status register, and phase encoding + */ + +#define ASC_CSR_INT 0x80 +#define ASC_CSR_GE 0x40 +#define ASC_CSR_PE 0x20 +#define ASC_CSR_TC 0x10 +#define ASC_CSR_VGC 0x08 +#define ASC_CSR_MSG 0x04 +#define ASC_CSR_CD 0x02 +#define ASC_CSR_IO 0x01 + +#define ASC_PHASE(csr) ((csr) & 0x7) +#define ASC_PHASE_DATAO 0x0 +#define ASC_PHASE_DATAI 0x1 +#define ASC_PHASE_COMMAND 0x2 +#define ASC_PHASE_STATUS 0x3 + /* 4..5 ANSI reserved */ +#define ASC_PHASE_MSG_OUT 0x6 +#define ASC_PHASE_MSG_IN 0x7 + +/* + * Destination Bus ID + */ + +#define ASC_DEST_ID_MASK 0x07 + +/* + * Interrupt register + */ + +#define ASC_INT_RESET 0x80 +#define ASC_INT_ILL 0x40 +#define ASC_INT_DISC 0x20 +#define ASC_INT_BS 0x10 +#define ASC_INT_FC 0x08 +#define ASC_INT_RESEL 0x04 +#define ASC_INT_SEL_ATN 0x02 +#define ASC_INT_SEL 0x01 + +/* + * Timeout register: + * + * val = (timeout * CLK_freq) / (8192 * CCF); + */ + +#define ASC_TIMEOUT_250(clk, ccf) (((clk) * 31) / (ccf)) + +/* + * Sequence Step register + */ + +#define ASC_SS_RESERVED 0xf0 +#define ASC_SS_SOM 0x08 +#define ASC_SS_MASK 0x07 +#define ASC_SS(ss) ((ss) & ASC_SS_MASK) + +/* + * Synchronous Transfer Period + */ + +#define ASC_STP_MASK 0x1f +#define ASC_STP_MIN 0x05 /* 5 clk per byte */ +#define ASC_STP_MAX 0x04 /* after ovfl, 35 clk/byte */ + +/* + * FIFO flags + */ + +#define ASC_FLAGS_SEQ_STEP 0xe0 +#define ASC_FLAGS_FIFO_CNT 0x1f + +/* + * Synchronous offset + */ + +#define ASC_SYNO_MASK 0x0f /* 0 -> asyn */ + +/* + * Configuration 1 + */ + +#define ASC_CNFG1_SLOW 0x80 +#define ASC_CNFG1_SRD 0x40 +#define ASC_CNFG1_P_TEST 0x20 +#define ASC_CNFG1_P_CHECK 0x10 +#define ASC_CNFG1_TEST 0x08 +#define ASC_CNFG1_MY_BUS_ID 0x07 + +/* + * CCF register + */ + +#define ASC_CCF(clk) ((((clk) - 1) / 5) + 1) + +/* + * Test register + */ + +#define ASC_TEST_XXXX 0xf8 +#define ASC_TEST_HI_Z 0x04 +#define ASC_TEST_I 0x02 +#define ASC_TEST_T 0x01 + +/* + * Configuration 2 + */ + +#define ASC_CNFG2_RFB 0x80 +#define ASC_CNFG2_EPL 0x40 +#define ASC_CNFG2_EBC 0x20 +#define ASC_CNFG2_DREQ_HIZ 0x10 +#define ASC_CNFG2_SCSI2 0x08 +#define ASC_CNFG2_BPA 0x04 +#define ASC_CNFG2_RPE 0x02 +#define ASC_CNFG2_DPE 0x01 + +/* + * Configuration 3 + */ + +#define ASC_CNFG3_RESERVED 0xf8 +#define ASC_CNFG3_SRB 0x04 +#define ASC_CNFG3_ALT_DMA 0x02 +#define ASC_CNFG3_T8 0x01 + +#define ST_MASK 0x3e diff --git a/sys/arch/wgrisc/dev/com_risc.c b/sys/arch/wgrisc/dev/com_risc.c new file mode 100644 index 00000000000..557179e6642 --- /dev/null +++ b/sys/arch/wgrisc/dev/com_risc.c @@ -0,0 +1,195 @@ +/* $OpenBSD: com_risc.c,v 1.1 1997/02/06 16:02:42 pefo Exp $ */ + +/* + * Copyright (c) 1993, 1994 Charles Hannum. + * Copyright (c) 1990 William F. Jolitz, TeleMuse + * 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 software is a component of "386BSD" developed by + * William F. Jolitz, TeleMuse. + * 4. Neither the name of the developer nor the name "386BSD" + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ + * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS + * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT. + * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT + * NOT MAKE USE OF THIS WORK. + * + * FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED + * BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN + * REFERENCES SUCH AS THE "PORTING UNIX TO THE 386" SERIES + * (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING + * JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND + * LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE + * ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS + * OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``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 DEVELOPER 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. + */ + +/* + * Device Driver for AT parallel printer port + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/tty.h> + +#include <machine/autoconf.h> +#include <machine/bus.h> +#include <machine/intr.h> + +#include <dev/isa/isavar.h> /* XXX for isa_chipset_tag_t in com_softc */ + +#include <dev/ic/comreg.h> +#include <dev/ic/comvar.h> +#include <dev/ic/ns16550reg.h> + +/* Macros to clear/set/test flags. */ +#define SET(t, f) (t) |= (f) +#define CLR(t, f) (t) &= ~(f) +#define ISSET(t, f) ((t) & (f)) + +#undef CONADDR /* This is stupid but using devs before config .. */ +#define CONADDR 0xae400000 + +int com_risc_probe __P((struct device *, void *, void *)); +void com_risc_attach __P((struct device *, struct device *, void *)); + +struct cfattach com_risc_ca = { + sizeof(struct com_softc), com_risc_probe, com_risc_attach +}; + +int +com_risc_probe(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + bus_space_tag_t iot; + bus_space_handle_t ioh; + int iobase, needioh; + int rv = 1; + struct confargs *ca = aux; + + if(!BUS_MATCHNAME(ca, "com")) + return(0); + iobase = (long)BUS_CVTADDR(ca); + iot = 0; + needioh = 1; + + /* if it's in use as console, it's there. */ + if (iobase == comconsaddr && !comconsattached) + goto out; + + if (needioh && bus_space_map(iot, iobase, COM_NPORTS, 0, &ioh)) { + rv = 0; + goto out; + } + rv = comprobe1(iot, ioh, iobase); + if (needioh) + bus_space_unmap(iot, ioh, COM_NPORTS); + +out: + return (rv); +} + +void +com_risc_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct com_softc *sc = (void *)self; + int iobase; + bus_space_tag_t iot; + bus_space_handle_t ioh; + struct confargs *ca = aux; + + sc->sc_hwflags = 0; + sc->sc_swflags = 0; + sc->sc_iobase = iobase = (bus_addr_t)BUS_CVTADDR(ca); + sc->sc_ioh = ioh = (bus_space_handle_t)iobase; + sc->sc_iot = iot = 0; + + if (iobase == comconsaddr) { + comconsattached = 1; + + /* + * Need to reset baud rate, etc. of next print so reset + * comconsinit. Also make sure console is always "hardwired". + */ + delay(1000); /* wait for output to finish */ + comconsinit = 0; + SET(sc->sc_hwflags, COM_HW_CONSOLE); + SET(sc->sc_swflags, COM_SW_SOFTCAR); + } + + + /* look for a NS 16550AF UART with FIFOs */ + bus_space_write_1(iot, ioh, com_fifo, + FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_14); + delay(100); + if (ISSET(bus_space_read_1(iot, ioh, com_iir), IIR_FIFO_MASK) == + IIR_FIFO_MASK) + if (ISSET(bus_space_read_1(iot, ioh, com_fifo), + FIFO_TRIGGER_14) == FIFO_TRIGGER_14) { + SET(sc->sc_hwflags, COM_HW_FIFO); + printf(": ns16550a, working fifo\n"); + } else + printf(": ns16550, broken fifo\n"); + else + printf(": ns8250 or ns16450, no fifo\n"); + bus_space_write_1(iot, ioh, com_fifo, 0); + + /* disable interrupts */ + bus_space_write_1(iot, ioh, com_ier, 0); + bus_space_write_1(iot, ioh, com_mcr, 0); + + BUS_INTR_ESTABLISH(ca, comintr, (void *)(long)sc); + +#ifdef KGDB + if (kgdb_dev == makedev(commajor, unit)) { + if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) + kgdb_dev = -1; /* can't debug over console port */ + else { + cominit(iot, ioh, kgdb_rate); + if (kgdb_debug_init) { + /* + * Print prefix of device name, + * let kgdb_connect print the rest. + */ + printf("%s: ", sc->sc_dev.dv_xname); + kgdb_connect(1); + } else + printf("%s: kgdb enabled\n", + sc->sc_dev.dv_xname); + } + } +#endif + + /* XXX maybe move up some? */ + if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) + printf("%s: console\n", sc->sc_dev.dv_xname); +} diff --git a/sys/arch/wgrisc/dev/dma.h b/sys/arch/wgrisc/dev/dma.h new file mode 100644 index 00000000000..dd5fb4c51b2 --- /dev/null +++ b/sys/arch/wgrisc/dev/dma.h @@ -0,0 +1,147 @@ +/* $OpenBSD: dma.h,v 1.1 1997/02/06 16:02:42 pefo Exp $ */ + +/* + * Copyright (c) 1996 Per Fogelstrom + * + * 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 under OpenBSD by + * Per Fogelstrom. + * 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. + * + */ + +extern vm_map_t phys_map; + +/* + * Structure used to control dma. + */ + +typedef struct dma_softc { + struct device sc_dev; /* use as a device */ + struct esp_softc *sc_esp; + int dma_ch; + vm_offset_t dma_va; /* Viritual address for transfer */ + vm_offset_t req_va; /* Original request va */ + int req_size; /* Request size */ + int mode; /* Mode register value and direction */ + int sc_active; /* Active flag */ + char **sc_dmaaddr; /* Pointer to dma address in dev */ + int *sc_dmalen; /* Pointer to len counter in dev */ +} dma_softc_t; + +#define DMA_TO_DEV 0 +#define DMA_FROM_DEV 1 + +#define DMA_CH0 0 +#define DMA_CH1 1 + +#define DMA_RESET(r) +#if 0 +#define DMA_START(a, b, c, d) \ + { \ + int dcmd; \ + int xcmd; \ + int pa; \ + int sz; \ + if((vm_offset_t)(b) < VM_MIN_KERNEL_ADDRESS) { \ + pa = CACHED_TO_PHYS(b); \ + } \ + else { \ + pa = pmap_extract(vm_map_pmap(phys_map), (vm_offset_t)(b));\ + } \ + sz = c; \ + if(sz + (pa & (NBPG - 1)) > NBPG) { \ + sz = NBPG - (pa & (NBPG - 1)); \ + } \ + dcmd = ((d) == DMA_FROM_DEV) ? 0x30 : 0x10; \ + if((a)->dma_ch == DMA_CH0) { \ + out32(R3715_DMA_ADR0, pa); \ + out32(R3715_DMA_CNT0, sz - 1); \ + xcmd = ~0x30; \ + } \ + else { \ + out32(R3715_DMA_ADR1, pa); \ + out32(R3715_DMA_CNT1, sz - 1); \ + dcmd = dcmd << 6; \ + xcmd = ~(0x30 << 6); \ + } \ + dcmd |= (1 << 26); \ + out32(R3715_IO_TIMING, (in32(R3715_IO_TIMING) & xcmd) | dcmd);\ + } +#else +#define DMA_START(a, b, c, d) \ + { \ + int dcmd; \ + int xcmd; \ + int pa; \ + int sz; \ + pa = CACHED_TO_PHYS(dma_buffer); \ + (a)->req_va = (vm_offset_t)(b); \ + (a)->req_size = c; \ + (a)->mode = d; \ + sz = c; \ + if((d) == DMA_TO_DEV) { \ + int *_p = (int *)PHYS_TO_UNCACHED(pa); \ + int *_v = (int *)b; \ + int _n = sz; \ + while(_n > 0) { \ + *_p = htonl(*_v); \ + _p++; _v++; _n -= 4; \ + } \ + } \ + dcmd = ((d) == DMA_FROM_DEV) ? 0x30 : 0x10; \ + if((a)->dma_ch == DMA_CH0) { \ + out32(R3715_DMA_ADR0, pa); \ + out32(R3715_DMA_CNT0, sz - 1); \ + xcmd = ~0x30; \ + } \ + else { \ + out32(R3715_DMA_ADR1, pa); \ + out32(R3715_DMA_CNT1, sz - 1); \ + dcmd = dcmd << 6; \ + xcmd = ~(0x30 << 6); \ + } \ + dcmd |= (1 << 26); \ + out32(R3715_IO_TIMING, (in32(R3715_IO_TIMING) & xcmd) | dcmd);\ + } +#endif +#define DMA_MAP(a, b, c, d) +#define DMA_INTR(r) +#define DMA_DRAIN(r) +#define DMA_END(c) \ + { \ + int dcmd; \ + dcmd = ((c)->dma_ch == DMA_CH0) ? 0x10 : 0x10000; \ + out32(R3715_IO_TIMING, in32(R3715_IO_TIMING) & ~dcmd); \ + if((c)->mode == DMA_FROM_DEV) { \ + int *_v = (int *)(c)->req_va; \ + int *_p = (int *)PHYS_TO_UNCACHED(CACHED_TO_PHYS(dma_buffer)); \ + int _n = (c)->req_size; \ + while(_n > 0) { \ + *_v = htonl(*_p); \ + _p++; _v++; _n -= 4; \ + } \ + } \ + } diff --git a/sys/arch/wgrisc/dev/if_sn.c b/sys/arch/wgrisc/dev/if_sn.c new file mode 100644 index 00000000000..b31ed2e0911 --- /dev/null +++ b/sys/arch/wgrisc/dev/if_sn.c @@ -0,0 +1,1288 @@ +/* $OpenBSD: if_sn.c,v 1.1 1997/02/06 16:02:42 pefo Exp $ */ +/* + * National Semiconductor SONIC Driver + * Copyright (c) 1991 Algorithmics Ltd (http://www.algor.co.uk) + * You may use, copy, and modify this program so long as you retain the + * copyright line. + * + * This driver has been substantially modified since Algorithmics donated + * it. + */ + +#include "sn.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/buf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/syslog.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/device.h> +#include <machine/autoconf.h> + +#include <net/if.h> +#include <net/netisr.h> +#include <net/route.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#endif + +#include <vm/vm.h> + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +#define SONICDW 32 +typedef unsigned char uchar; + +#include <wgrisc/dev/if_sn.h> +#define SWR(a, x) (a) = (x) +#define SRD(a) ((a) & 0xffff) + +#include <machine/cpu.h> +#include <wgrisc/riscbus/riscbus.h> + +/* + * Statistics collected over time + */ +struct sn_stats { + int ls_opacks; /* packets transmitted */ + int ls_ipacks; /* packets received */ + int ls_tdr; /* contents of tdr after collision */ + int ls_tdef; /* packets where had to wait */ + int ls_tone; /* packets with one retry */ + int ls_tmore; /* packets with more than one retry */ + int ls_tbuff; /* transmit buff errors */ + int ls_tuflo; /* " uflo " */ + int ls_tlcol; + int ls_tlcar; + int ls_trtry; + int ls_rbuff; /* receive buff errors */ + int ls_rfram; /* framing */ + int ls_roflo; /* overflow */ + int ls_rcrc; + int ls_rrng; /* rx ring sequence error */ + int ls_babl; /* chip babl error */ + int ls_cerr; /* collision error */ + int ls_miss; /* missed packet */ + int ls_merr; /* memory error */ + int ls_copies; /* copies due to out of range mbufs */ + int ls_maxmbufs; /* max mbufs on transmit */ + int ls_maxslots; /* max ring slots on transmit */ +}; + +struct sn_softc { + struct device sc_dev; + struct arpcom sc_ac; +#define sc_if sc_ac.ac_if /* network visible interface */ +#define sc_enaddr sc_ac.ac_enaddr /* hardware ethernet address */ + + struct sonic_reg *sc_csr; /* hardware pointer */ + int sc_rxmark; /* position in rx ring for reading buffs */ + + int sc_rramark; /* index into rra of wp */ + + int sc_txhead; /* index of first TDA passed to chip */ + int sc_missed; /* missed packet counter */ + struct RXpkt *sc_lrxp; /* last RDA available to chip */ + struct sn_stats sc_sum; + short sc_iflags; +} sn_softc; + +int snmatch __P((struct device *, void *, void *)); +void snattach __P((struct device *, struct device *, void *)); + +struct cfattach sn_ca = { + sizeof(struct sn_softc), snmatch, snattach +}; +struct cfdriver sn_cd = { + NULL, "sn", DV_IFNET, NULL, 0 +}; + +#undef assert +#undef _assert + +#ifdef NDEBUG +#define assert(e) ((void)0) +#define _assert(e) ((void)0) +#else +#define _assert(e) assert(e) +#ifdef __STDC__ +#define assert(e) ((e) ? (void)0 : __assert("sn ", __FILE__, __LINE__, #e)) +#else /* PCC */ +#define assert(e) ((e) ? (void)0 : __assert("sn "__FILE__, __LINE__, "e")) +#endif +#endif + +void +m_check(m) + struct mbuf *m; +{ + if (m->m_flags & M_EXT) { + assert(m->m_len >= 0); + assert(m->m_len <= m->m_ext.ext_size); + assert(m->m_data >= &m->m_ext.ext_buf[0]); + assert(m->m_data <= &m->m_ext.ext_buf[m->m_ext.ext_size]); + assert(m->m_data + m->m_len <= &m->m_ext.ext_buf[m->m_ext.ext_size]); + } else if (m->m_flags & M_PKTHDR) { + assert(m->m_len >= 0); + assert(m->m_len <= MHLEN); + assert(m->m_data >= m->m_pktdat); + assert(m->m_data <= &m->m_pktdat[MHLEN]); + assert(m->m_data + m->m_len <= &m->m_pktdat[MHLEN]); + } else { + assert(m->m_len >= 0); + assert(m->m_len <= MLEN); + assert(m->m_data >= m->m_dat); + assert(m->m_data <= &m->m_dat[MLEN]); + assert(m->m_data + m->m_len <= &m->m_dat[MLEN]); + } +} + +void +m_checkm(m) + struct mbuf *m; +{ + while (m) { + m_check(m); + m = m->m_next; + } +} + +int ethdebug = 0; + +int snintr __P((struct sn_softc *)); +int snioctl __P((struct ifnet *ifp, u_long cmd, caddr_t data)); +void snstart __P((struct ifnet *ifp)); +void snwatchdog __P((struct ifnet *ifp)); +void snreset __P((struct sn_softc *sc)); + +/* + * SONIC buffers need to be aligned 16 or 32 bit aligned. + * These macros calculate and verify alignment. + */ +#if SONICDW == 32 +#define SONICALIGN 4 +#else +#define SONICALIGN 2 +#endif +#define SOALIGN(array) (((int)array+SONICALIGN-1) & ~(SONICALIGN-1)) +#define SOALIGNED(p) (!(((uint)p)&(SONICALIGN-1))) + +#define UPPER(x) ((unsigned)(x) >> 16) +#define LOWER(x) ((unsigned)(x) & 0xffff) + +#define NRRA 32 /* # receive resource descriptors */ +#define RRAMASK 0x1f /* why it must be poer of two */ + +#define NRBA 16 /* # receive buffers < NRRA */ +#define NRDA NRBA /* # receive descriptors */ +#define NTDA 4 /* # transmit descriptors */ + +#define CDASIZE sizeof(struct CDA) +#define RRASIZE (NRRA*sizeof(struct RXrsrc)) +#define RDASIZE (NRDA*sizeof(struct RXpkt)) +#define TDASIZE (NTDA*sizeof(struct TXpkt)) + +#define FCSSIZE 4 /* size of FCS append te received packets */ + +/* + * maximum recieve packet size plus 2 byte pad to make each + * one aligned. 4 byte slop (required for eobc) + */ +#define RBASIZE (sizeof(struct ether_header) + ETHERMTU + FCSSIZE + 2 + 4) + +/* + * space requiered for descriptors + */ +#define DESC_SIZE (RRASIZE + CDASIZE + RDASIZE + TDASIZE + SONICALIGN - 1) + +/* + * Nicely aligned pointers into the sonicbuffers + * p_ points at physical (K1_SEG) addresses. + * v_ is sonic local address used by sonic. + */ +struct RXrsrc *p_rra; /* receiver resource descriptors */ +struct RXrsrc *v_rra; +struct RXpkt *p_rda; /* receiver desriptors */ +struct RXpkt *v_rda; +struct TXpkt *p_tda; /* transmitter descriptors */ +struct TXpkt *v_tda; +struct CDA *p_cda; /* CAM descriptors */ +struct CDA *v_cda; +char *p_rba; /* receive buffer area base */ +char *v_rba; + +/* Meta transmit descriptors */ +struct mtd { + struct mtd *mtd_link; + struct TXpkt *mtd_txp; + struct mbuf *mtd_mbuf; +} mtda[NTDA]; + +struct mtd *mtdfree; /* list of free meta transmit descriptors */ +struct mtd *mtdhead; /* head of descriptors assigned to chip */ +struct mtd *mtdtail; /* tail of descriptors assigned to chip */ +struct mtd *mtdnext; /* next descriptor to give to chip */ + +void mtd_free __P((struct mtd *)); +struct mtd *mtd_alloc __P((void)); + +int sngetaddr __P((struct sn_softc *sc)); +int sninit __P((struct sn_softc *sc)); +int snstop __P((struct sn_softc *sc)); +int sonicput __P((struct sn_softc *sc, struct mbuf *m0)); + +void camdump __P((struct sn_softc *sc)); + +int +snmatch(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct cfdata *cf = match; + struct confargs *ca = aux; + + /* XXX CHECK BUS */ + /* make sure that we're looking for this type of device. */ + if (!BUS_MATCHNAME(ca, "sonic")) + return (0); + + return (1); +} + +/* + * Interface exists: make available by filling in network interface + * record. System will initialize the interface when it is ready + * to accept packets. + */ +void +snattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct sn_softc *sc = (void *)self; + struct confargs *ca = aux; + struct ifnet *ifp = &sc->sc_if; + struct cfdata *cf = sc->sc_dev.dv_cfdata; + int p, pp; + + sc->sc_csr = (struct sonic_reg *)BUS_CVTADDR(ca); + +/* + * because the sonic is basicly 16bit device it 'concatenates' + * a higher buffer address to a 16 bit offset this will cause wrap + * around problems near the end of 64k !! + */ + p = RISC_SONIC_SRAM; + pp = RISC_SONIC_SRAM - (FRAGMAX * NTDA * 4096); + + if ((p ^ (p + TDASIZE)) & 0x10000) + p = (p + 0x10000) & ~0xffff; + p_tda = (struct TXpkt *) p; + v_tda = (struct TXpkt *)(p - pp); + p += TDASIZE; + + if ((p ^ (p + RRASIZE + CDASIZE)) & 0x10000) + p = (p + 0x10000) & ~0xffff; + p_rra = (struct RXrsrc *) p; + v_rra = (struct RXrsrc *)(p - pp); + p += RRASIZE; + + if ((p ^ (p + RDASIZE)) & 0x10000) + p = (p + 0x10000) & ~0xffff; + p_rda = (struct RXpkt *) p; + v_rda = (struct RXpkt *)(p - pp); + p += RDASIZE; + + p_cda = (struct CDA *) p; + v_cda = (struct CDA *)(p - pp); + p += CDASIZE; + + p += 4096 - (p & (4095)); + p_rba = (char *)p; + v_rba = (char *)(p - pp); + p += NRBA * RBASIZE; + + printf(": bufsize %d",p - RISC_SONIC_SRAM); + +#if 0 + camdump(sc); +#endif + sngetaddr(sc); + printf(" address %s\n", ether_sprintf(sc->sc_enaddr)); + +#if 0 +printf("\nsonic buffers: rra=0x%x cda=0x%x rda=0x%x tda=0x%x rba=0x%x\n", + p_rra, p_cda, p_rda, p_tda, p_rba); +printf("sonic buffers: rra=0x%x cda=0x%x rda=0x%x tda=0x%x rba=0x%x\n", + v_rra, v_cda, v_rda, v_tda, v_rba); +printf("mapped to offset 0x%x size 0x%x\n", SONICBUF - pp, p - SONICBUF); +#endif + + BUS_INTR_ESTABLISH(ca, (intr_handler_t)snintr, (void *)sc); + + bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); + ifp->if_softc = sc; + ifp->if_ioctl = snioctl; + ifp->if_start = snstart; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_watchdog = snwatchdog; +#if NBPFILTER > 0 + bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif + if_attach(ifp); + ether_ifattach(ifp); +} + +int +snioctl(ifp, cmd, data) + struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + struct ifaddr *ifa; + struct sn_softc *sc = ifp->if_softc; + int s = splnet(), err = 0; + int temp; + int error; + + if ((error = ether_ioctl(ifp, &sc->sc_ac, cmd, data)) > 0) { + splx(s); + return error; + } + + switch (cmd) { + + case SIOCSIFADDR: + ifa = (struct ifaddr *)data; + ifp->if_flags |= IFF_UP; + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + (void)sninit(ifp->if_softc); + arp_ifinit(&sc->sc_ac, ifa); + break; +#endif + default: + (void)sninit(ifp->if_softc); + break; + } + break; + + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_UP) == 0 && + ifp->if_flags & IFF_RUNNING) { + snstop(ifp->if_softc); + ifp->if_flags &= ~IFF_RUNNING; + } else if (ifp->if_flags & IFF_UP && + (ifp->if_flags & IFF_RUNNING) == 0) + (void)sninit(ifp->if_softc); + /* + * If the state of the promiscuous bit changes, the interface + * must be reset to effect the change. + */ + if (((ifp->if_flags ^ sc->sc_iflags) & IFF_PROMISC) && + (ifp->if_flags & IFF_RUNNING)) { + sc->sc_iflags = ifp->if_flags; + printf("change in flags\n"); + temp = sc->sc_if.if_flags & IFF_UP; + snreset(sc); + sc->sc_if.if_flags |= temp; + snstart(ifp); + } + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + if(cmd == SIOCADDMULTI) + err = ether_addmulti((struct ifreq *)data, &sc->sc_ac); + else + err = ether_delmulti((struct ifreq *)data, &sc->sc_ac); + + if (err == ENETRESET) { + /* + * Multicast list has changed; set the hardware + * filter accordingly. But remember UP flag! + */ + temp = sc->sc_if.if_flags & IFF_UP; + snreset(sc); + sc->sc_if.if_flags |= temp; + err = 0; + } + break; + default: + err = EINVAL; + } + splx(s); + return (err); +} + +/* + * Encapsulate a packet of type family for the local net. + * Use trailer local net encapsulation if enough data in first + * packet leaves a multiple of 512 bytes of data in remainder. + */ +void +snstart(ifp) + struct ifnet *ifp; +{ + struct sn_softc *sc = ifp->if_softc; + struct mbuf *m; + int len; + + if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) + return; + IF_DEQUEUE(&sc->sc_if.if_snd, m); + if (m == 0) + return; + + /* + * If there is nothing in the o/p queue, and there is room in + * the Tx ring, then send the packet directly. Otherwise append + * it to the o/p queue. + */ + if (!sonicput(sc, m)) { /* not enough space */ + IF_PREPEND(&sc->sc_if.if_snd, m); + } +#if NBPFILTER > 0 + /* + * If bpf is listening on this interface, let it + * see the packet before we commit it to the wire. + */ + if (sc->sc_if.if_bpf) + bpf_mtap(sc->sc_if.if_bpf, m); +#endif + + sc->sc_if.if_opackets++; /* # of pkts */ + sc->sc_sum.ls_opacks++; /* # of pkts */ +} + +/* + * This is called from sonicioctl() when /etc/ifconfig is run to set + * the address or switch the i/f on. + */ +void caminitialise __P((void)); +void camentry __P((int, u_char *ea)); +void camprogram __P((struct sn_softc *)); +void initialise_tda __P((struct sn_softc *)); +void initialise_rda __P((struct sn_softc *)); +void initialise_rra __P((struct sn_softc *)); + +/* + * reset and restart the SONIC. Called in case of fatal + * hardware/software errors. + */ +void +snreset(sc) + struct sn_softc *sc; +{ + snstop(sc); + sninit(sc); +} + +int +sninit(sc) + struct sn_softc *sc; +{ + struct sonic_reg *csr = sc->sc_csr; + int s, error; + + if (sc->sc_if.if_flags & IFF_RUNNING) + /* already running */ + return (0); + + s = splnet(); + + csr->s_cr = CR_RST; /* s_dcr only accessable reset mode! */ + + /* config it */ + csr->s_dcr = DCR_LBR | DCR_SYNC | DCR_WAIT0 | DCR_DW32 | DCR_DMABLOCK | + DCR_RFT16 | DCR_TFT16; + csr->s_rcr = RCR_BRD | RCR_LBNONE; + csr->s_imr = IMR_PRXEN | IMR_PTXEN | IMR_TXEREN | IMR_HBLEN | IMR_LCDEN; + + /* clear pending interrupts */ + csr->s_isr = 0x7fff; + + /* clear tally counters */ + csr->s_crct = -1; + csr->s_faet = -1; + csr->s_mpt = -1; + + initialise_tda(sc); + initialise_rda(sc); + initialise_rra(sc); + + /* enable the chip */ + csr->s_cr = 0; + wbflush(); + + /* program the CAM with our address */ + caminitialise(); + camentry(0, sc->sc_enaddr); + camprogram(sc); + + /* get it to read resource descriptors */ + csr->s_cr = CR_RRRA; + wbflush(); + while (csr->s_cr & CR_RRRA) + continue; + + /* enable rx */ + csr->s_cr = CR_RXEN; + wbflush(); + + /* flag interface as "running" */ + sc->sc_if.if_flags |= IFF_RUNNING; + + splx(s); + return (0); + +bad: + snstop(sc); + return (error); +} + +/* + * close down an interface and free its buffers + * Called on final close of device, or if sninit() fails + * part way through. + */ +int +snstop(sc) + struct sn_softc *sc; +{ + struct mtd *mtd; + int s = splnet(); + + /* stick chip in reset */ + sc->sc_csr->s_cr = CR_RST; + wbflush(); + + /* free all receive buffers (currently static so nothing to do) */ + + /* free all pending transmit mbufs */ + while (mtd = mtdhead) { + mtdhead = mtdhead->mtd_link; + if (mtd->mtd_mbuf) + m_freem(mtd->mtd_mbuf); + mtd->mtd_mbuf = 0; + mtd_free(mtd); + } + mtdnext = mtd_alloc(); + + sc->sc_if.if_timer = 0; + sc->sc_if.if_flags &= ~(IFF_RUNNING | IFF_UP); + + splx(s); + return (0); +} + +/* + * Called if any Tx packets remain unsent after 5 seconds, + * In all cases we just reset the chip, and any retransmission + * will be handled by higher level protocol timeouts. + */ +void +snwatchdog(ifp) + struct ifnet *ifp; +{ + struct sn_softc *sc = ifp->if_softc; + int temp; + + if (mtdhead && mtdhead->mtd_mbuf) { + /* something still pending for transmit */ + if (mtdhead->mtd_txp->status == 0) + log(LOG_ERR, "%s: Tx - timeout\n", + sc->sc_if.if_xname); + else + log(LOG_ERR, "%s: Tx - lost interrupt\n", + sc->sc_if.if_xname); + temp = sc->sc_if.if_flags & IFF_UP; + snreset(sc); + sc->sc_if.if_flags |= temp; + } +} +/* + * stuff packet into sonic (at splnet) +*/ +int +sonicput(sc, m0) + struct sn_softc *sc; + struct mbuf *m0; +{ + struct sonic_reg *csr = sc->sc_csr; + struct TXpkt *txp; + struct mtd *mtdnew; + struct mbuf *m; + int len = 0, fr = 0; + int i; + int fragoffset; /* Offset in viritual dma space for fragment */ + + /* grab the replacement mtd */ + if ((mtdnew = mtd_alloc()) == 0) + return (0); + + /* this packet goes to mdtnext fill in the TDA */ + mtdnext->mtd_mbuf = m0; + txp = mtdnext->mtd_txp; + SWR(txp->config, 0); + fragoffset = (txp - p_tda) * FRAGMAX * 4096; + + /* + * Now fill in the fragments. Each fragment maps to it's + * own dma page. Fragments crossing a dma page boundary + * are split up in two fragments. This is somewhat stupid + * because the dma mapper can do the work, but it helps + * keeping the fragments in order. (read lazy programmer). + */ + for (m = m0; m; m = m->m_next) { + unsigned va = (unsigned) mtod(m, caddr_t); + int resid = m->m_len; + +#if has_to_be_fixed + if(resid != 0) { + R4K_HitFlushDCache(va, resid); + } +#endif + len += resid; + + while (resid) { + unsigned pa; + unsigned n; + + pa = (va & PGOFSET) + fragoffset; + n = resid; + if (n > NBPG - (va & PGOFSET)) { + n = NBPG - (va & PGOFSET); + } + if (fr < FRAGMAX) { + SWR(txp->u[fr].frag_ptrlo, LOWER(pa)); + SWR(txp->u[fr].frag_ptrhi, UPPER(pa)); + SWR(txp->u[fr].frag_size, n); + } + fr++; + va += n; + resid -= n; + fragoffset += 4096; + } + } + /* + * pad out last fragment for minimum size + */ + if (len < ETHERMIN + sizeof(struct ether_header) && fr < FRAGMAX) { + int pad = ETHERMIN + sizeof(struct ether_header) - len; + static char zeros[64]; + unsigned pa; + + pa = ((unsigned)zeros & PGOFSET) + fragoffset; + SWR(txp->u[fr].frag_ptrlo, LOWER(pa)); + SWR(txp->u[fr].frag_ptrhi, UPPER(pa)); + SWR(txp->u[fr].frag_size, pad); + fr++; + len = ETHERMIN + sizeof(struct ether_header); + } + + if (fr > FRAGMAX) { + mtd_free(mtdnew); + m_freem(m0); + log(LOG_ERR, "%s: tx too many fragments %d\n", + sc->sc_if.if_xname, fr); + sc->sc_if.if_oerrors++; + return (len); + } + + SWR(txp->frag_count, fr); + SWR(txp->pkt_size, len); + + /* link onto the next mtd that will be used */ + SWR(txp->u[fr].tlink, LOWER(v_tda + (mtdnew->mtd_txp - p_tda)) | EOL); + + if (mtdhead == 0) { + /* no current transmit list start with this one */ + mtdtail = mtdhead = mtdnext; + csr->s_ctda = LOWER(v_tda + (txp - p_tda)); + } else { + /* + * have a transmit list append it to end note + * mtdnext is already physicaly linked to mtdtail in + * mtdtail->mtd_txp->u[mtdtail->mtd_txp->frag_count].tlink + */ + SWR(mtdtail->mtd_txp->u[mtdtail->mtd_txp->frag_count].tlink, + SRD(mtdtail->mtd_txp->u[mtdtail->mtd_txp->frag_count].tlink) & ~EOL); + mtdtail = mtdnext; + } + mtdnext->mtd_link = mtdnew; + mtdnext = mtdnew; + + /* make sure chip is running */ + wbflush(); + csr->s_cr = CR_TXP; + wbflush(); + sc->sc_if.if_timer = 5; /* 5 seconds to watch for failing to transmit */ + return (len); +} + +/* + * Read out the ethernet address from the cam. It is stored + * there by the boot when doing a loopback test. Thus we don't + * have to fetch it from nv ram. + */ +int +sngetaddr(sc) + struct sn_softc *sc; +{ + unsigned i, x, y; + char *cp, *ea; + +#if 0 + sc->sc_csr->s_cr = CR_RST; + wbflush(); + sc->sc_csr->s_cep = 0; + i = sc->sc_csr->s_cap2; + wbflush(); + sc->sc_enaddr[5] = i >> 8; + sc->sc_enaddr[4] = i; + i = sc->sc_csr->s_cap1; + wbflush(); + sc->sc_enaddr[3] = i >> 8; + sc->sc_enaddr[2] = i; + i = sc->sc_csr->s_cap0; + wbflush(); + sc->sc_enaddr[1] = i >> 8; + sc->sc_enaddr[0] = i; + + sc->sc_csr->s_cr = 0; + wbflush(); +#else + sc->sc_enaddr[0] = 0x08; + sc->sc_enaddr[1] = 0x00; + sc->sc_enaddr[2] = 0x20; + sc->sc_enaddr[3] = 0xa0; + sc->sc_enaddr[4] = 0x66; + sc->sc_enaddr[5] = 0x54; +#endif + return (0); +} + +void sonictxint __P((struct sn_softc *)); +void sonicrxint __P((struct sn_softc *)); + +int sonic_read __P((struct sn_softc *, struct RXpkt *)); +struct mbuf *sonic_get __P((struct sn_softc *, struct ether_header *, int)); + +void +mtd_free(mtd) + struct mtd *mtd; +{ + mtd->mtd_link = mtdfree; + mtdfree = mtd; +} + +struct mtd * +mtd_alloc() +{ + struct mtd *mtd = mtdfree; + + if (mtd) { + mtdfree = mtd->mtd_link; + mtd->mtd_link = 0; + } + return (mtd); +} + +/* + * CAM support + */ +void +caminitialise() +{ + int i; + + for (i = 0; i < MAXCAM; i++) + SWR(p_cda->desc[i].cam_ep, i); + SWR(p_cda->enable, 0); +} + +void +camentry(entry, ea) + int entry; + u_char *ea; +{ + SWR(p_cda->desc[entry].cam_ep, entry); + SWR(p_cda->desc[entry].cam_ap2, (ea[5] << 8) | ea[4]); + SWR(p_cda->desc[entry].cam_ap1, (ea[3] << 8) | ea[2]); + SWR(p_cda->desc[entry].cam_ap0, (ea[1] << 8) | ea[0]); + SWR(p_cda->enable, SRD(p_cda->enable) | (1 << entry)); +} + +void +camprogram(sc) + struct sn_softc *sc; +{ + struct sonic_reg *csr; + int timeout; + int i; + + csr = sc->sc_csr; + csr->s_cdp = LOWER(v_cda); + csr->s_cdc = MAXCAM; + csr->s_cr = CR_LCAM; + wbflush(); + + timeout = 10000; + while (csr->s_cr & CR_LCAM && timeout--) + continue; + if (timeout == 0) { + /* XXX */ + panic("sonic: CAM initialisation failed\n"); + } + timeout = 10000; + while ((csr->s_isr & ISR_LCD) == 0 && timeout--) + continue; + + if (csr->s_isr & ISR_LCD) + csr->s_isr = ISR_LCD; + else + printf("sonic: CAM initialisation without interrupt\n"); +} + +#if 0 +void +camdump(sc) + struct sn_softc *sc; +{ + struct sonic_reg *csr = sc->sc_csr; + int i; + + printf("CAM entries:\n"); + csr->s_cr = CR_RST; + wbflush(); + + for (i = 0; i < 16; i++) { + ushort ap2, ap1, ap0; + csr->s_cep = i; + wbflush(); + ap2 = csr->s_cap2; + ap1 = csr->s_cap1; + ap0 = csr->s_cap0; + printf("%d: ap2=0x%x ap1=0x%x ap0=0x%x\n", i, ap2, ap1, ap0); + } + printf("CAM enable 0x%x\n", csr->s_cep); + + csr->s_cr = 0; + wbflush(); +} +#endif + +void +initialise_tda(sc) + struct sn_softc *sc; +{ + struct sonic_reg *csr; + struct mtd *mtd; + int i; + + csr = sc->sc_csr; + + mtdfree = mtdhead = mtdtail = (struct mtd *) 0; + + for (i = 0; i < NTDA; i++) { + mtd = &mtda[i]; + mtd->mtd_txp = &p_tda[i]; + mtd->mtd_mbuf = (struct mbuf *) 0; + mtd_free(mtd); + } + mtdnext = mtd_alloc(); + + csr->s_utda = UPPER(v_tda); +} + +void +initialise_rda(sc) + struct sn_softc *sc; +{ + struct sonic_reg *csr; + int i; + + csr = sc->sc_csr; + + /* link the RDA's together into a circular list */ + for (i = 0; i < (NRDA - 1); i++) { + SWR(p_rda[i].rlink, LOWER(&v_rda[i + 1])); + SWR(p_rda[i].in_use, 1); + } + SWR(p_rda[NRDA - 1].rlink, LOWER(&v_rda[0]) | EOL); + SWR(p_rda[NRDA - 1].in_use, 1); + + /* mark end of receive descriptor list */ + sc->sc_lrxp = &p_rda[NRDA - 1]; + + sc->sc_rxmark = 0; + + csr->s_urda = UPPER(&v_rda[0]); + csr->s_crda = LOWER(&v_rda[0]); + wbflush(); +} + +void +initialise_rra(sc) + struct sn_softc *sc; +{ + struct sonic_reg *csr; + int i; + + csr = sc->sc_csr; + + csr->s_eobc = RBASIZE / 2 - 2; /* must be >= MAXETHERPKT */ + csr->s_urra = UPPER(v_rra); + csr->s_rsa = LOWER(v_rra); + csr->s_rea = LOWER(&v_rra[NRRA]); + csr->s_rrp = LOWER(v_rra); + csr->s_rsc = 0; + + /* fill up SOME of the rra with buffers */ + for (i = 0; i < NRBA; i++) { + SWR(p_rra[i].buff_ptrhi, UPPER(&v_rba[i * RBASIZE])); + SWR(p_rra[i].buff_ptrlo, LOWER(&v_rba[i * RBASIZE])); + SWR(p_rra[i].buff_wchi, UPPER(RBASIZE / 2)); + SWR(p_rra[i].buff_wclo, LOWER(RBASIZE / 2)); + } + sc->sc_rramark = NRBA; + csr->s_rwp = LOWER(&v_rra[sc->sc_rramark]); + wbflush(); +} + +int +snintr(sc) + struct sn_softc *sc; +{ + struct sonic_reg *csr = sc->sc_csr; + int isr; + + while (isr = (csr->s_isr & ISR_ALL)) { + /* scrub the interrupts that we are going to service */ + csr->s_isr = isr; + wbflush(); + + if (isr & (ISR_BR | ISR_LCD | ISR_PINT | ISR_TC)) + printf("sonic: unexpected interrupt status 0x%x\n", isr); + + if (isr & (ISR_TXDN | ISR_TXER)) + sonictxint(sc); + + if (isr & ISR_PKTRX) + sonicrxint(sc); + + if (isr & (ISR_HBL | ISR_RDE | ISR_RBE | ISR_RBAE | ISR_RFO)) { + if (isr & ISR_HBL) + printf("sonic: no heartbeat\n"); + if (isr & ISR_RDE) + printf("sonic: receive descriptors exhausted\n"); + if (isr & ISR_RBE) + printf("sonic: receive buffers exhausted\n"); + if (isr & ISR_RBAE) + printf("sonic: receive buffer area exhausted\n"); + if (isr & ISR_RFO) + printf("sonic: receive FIFO overrun\n"); + } + if (isr & (ISR_CRC | ISR_FAE | ISR_MP)) { +#ifdef notdef + if (isr & ISR_CRC) + sc->sc_crctally++; + if (isr & ISR_FAE) + sc->sc_faetally++; + if (isr & ISR_MP) + sc->sc_mptally++; +#endif + } + } + return (1); +} + +/* + * Transmit interrupt routine + */ +void +sonictxint(sc) + struct sn_softc *sc; +{ + struct TXpkt *txp; + struct sonic_reg *csr; + struct mtd *mtd; + + if (mtdhead == (struct mtd *) 0) + return; + + csr = sc->sc_csr; + + while (mtd = mtdhead) { + struct mbuf *m = mtd->mtd_mbuf; + + if (m == 0) + break; + + txp = mtd->mtd_txp; + + if (SRD(txp->status) == 0) /* it hasn't really gone yet */ + return; + + if (ethdebug) { + struct ether_header *eh = mtod(m, struct ether_header *); + printf("xmit status=0x%x len=%d type=0x%x from %s", + txp->status, + txp->pkt_size, + htons(eh->ether_type), + ether_sprintf(eh->ether_shost)); + printf(" (to %s)\n", ether_sprintf(eh->ether_dhost)); + } + m_freem(m); + mtd->mtd_mbuf = 0; + mtdhead = mtd->mtd_link; + + mtd_free(mtd); + + if ((SRD(txp->status) & TCR_PTX) == 0) { + printf("sonic: Tx packet status=0x%x\n", txp->status); + + if (mtdhead != mtdnext) { + printf("resubmitting remaining packets\n"); + csr->s_ctda = LOWER(v_tda + (mtdhead->mtd_txp - p_tda)); + csr->s_cr = CR_TXP; + wbflush(); + return; + } + } + } + /* mtdhead should be at mtdnext (go) */ + assert(mtdhead == mtdnext); + assert(mtdhead->mtd_link == 0); + mtdhead = 0; + + /* and start feeding any queued packets to chip */ + while (1) { + struct mbuf *m; + + IF_DEQUEUE(&sc->sc_if.if_snd, m); + if (m == 0) /* nothing left to send */ + break; + if (!sonicput(sc, m)) { /* not enough space */ + IF_PREPEND(&sc->sc_if.if_snd, m); + break; + } + } +} + +/* + * Receive interrupt routine + */ +void +sonicrxint(sc) + struct sn_softc *sc; +{ + struct sonic_reg *csr = sc->sc_csr; + struct RXpkt *rxp; + u_long addr; + int orra; + + rxp = &p_rda[sc->sc_rxmark]; + + while (SRD(rxp->in_use) == 0) { + unsigned status = SRD(rxp->status); + if ((status & RCR_LPKT) == 0) + printf("sonic: more than one packet in RBA!\n"); + assert(PSNSEQ(SRD(rxp->seq_no)) == 0); + + if (status & RCR_PRX) { + if (sonic_read(sc, rxp)) { + sc->sc_if.if_ipackets++; + sc->sc_sum.ls_ipacks++; + sc->sc_missed = 0; + } + } else + sc->sc_if.if_ierrors++; + + /* + * give receive buffer area back to chip XXX what buffer + * did the sonic use for this descriptor answer look at + * the rba sequence number !! + */ + orra = RBASEQ(SRD(rxp->seq_no)) & RRAMASK; + + assert(SRD(rxp->pkt_ptrhi) == SRD(p_rra[orra].buff_ptrhi)); + assert(SRD(rxp->pkt_ptrlo) == SRD(p_rra[orra].buff_ptrlo)); +if(SRD(rxp->pkt_ptrlo) != SRD(p_rra[orra].buff_ptrlo)) +printf("%x,%x\n",SRD(rxp->pkt_ptrlo),SRD(p_rra[orra].buff_ptrlo)); + assert(SRD(p_rra[orra].buff_wclo)); + + /* + * orra is now empty of packets and can be freed if + * sonic read didnt copy it out then we would have to + * wait !! + * (dont bother add it back in again straight away) + */ + p_rra[sc->sc_rramark] = p_rra[orra]; + + /* zap old rra for fun */ + p_rra[orra].buff_wchi = 0; + p_rra[orra].buff_wclo = 0; + + sc->sc_rramark = (sc->sc_rramark + 1) & RRAMASK; + csr->s_rwp = LOWER(&v_rra[sc->sc_rramark]); + wbflush(); + + /* + * give recieve descriptor back to chip simple + * list is circular + */ + SWR(rxp->in_use, 1); + SWR(rxp->rlink, SRD(rxp->rlink) | EOL); + SWR(sc->sc_lrxp->rlink, SRD(sc->sc_lrxp->rlink) & ~EOL); + sc->sc_lrxp = rxp; + + if (++sc->sc_rxmark >= NRDA) + sc->sc_rxmark = 0; + rxp = &p_rda[sc->sc_rxmark]; + } +} + +/* + * sonic_read -- pull packet off interface and forward to + * appropriate protocol handler + */ +int +sonic_read(sc, rxp) + struct sn_softc *sc; + struct RXpkt *rxp; +{ + struct ifnet *ifp = &sc->sc_if; + /*extern char *ether_sprintf();*/ + struct ether_header *et; + struct mbuf *m; + int len, off, i; + caddr_t pkt; + + /* + * Get input data length. + * Get pointer to ethernet header (in input buffer). + * Deal with trailer protocol: if type is PUP trailer + * get true type from first 16-bit word past data. + * Remember that type was trailer by setting off. + */ + + len = SRD(rxp->byte_count) - sizeof(struct ether_header) - FCSSIZE; + pkt = (caddr_t)((SRD(rxp->pkt_ptrhi) << 16) | SRD(rxp->pkt_ptrlo)); + pkt = pkt - v_rba + p_rba; + et = (struct ether_header *)pkt; + + if (ethdebug) { + printf("rcvd 0x%x status=0x%x, len=%d type=0x%x from %s", + et, rxp->status, len, htons(et->ether_type), + ether_sprintf(et->ether_shost)); + printf(" (to %s)\n", ether_sprintf(et->ether_dhost)); + } + if (len < ETHERMIN || len > ETHERMTU) { + printf("sonic: invalid packet length %d bytes\n", len); + return (0); + } + +#if NBPFILTER > 0 + /* + * Check if there's a bpf filter listening on this interface. + * If so, hand off the raw packet to enet, then discard things + * not destined for us (but be sure to keep broadcast/multicast). + */ + if (sc->sc_if.if_bpf) { + bpf_tap(sc->sc_if.if_bpf, pkt, + len + sizeof(struct ether_header)); + if ((ifp->if_flags & IFF_PROMISC) != 0 && + (et->ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */ + bcmp(et->ether_dhost, sc->sc_enaddr, + sizeof(et->ether_dhost)) != 0) + return; + } +#endif + m = sonic_get(sc, et, len); + if (m == NULL) + return (0); + ether_input(ifp, et, m); + return(1); +} + +#define sonicdataaddr(eh, off, type) ((type)(((caddr_t)((eh)+1)+(off)))) + +/* + * munge the recieved packet into an mbuf chain + * because we are using stupif buffer management this + * is slow. +*/ +struct mbuf * +sonic_get(sc, eh, datalen) + struct sn_softc *sc; + struct ether_header *eh; + int datalen; +{ + struct mbuf *m; + struct mbuf *top = 0, **mp = ⊤ + int len; + char *spkt = sonicdataaddr(eh, 0, caddr_t); + char *epkt = spkt + datalen; + char *cp = spkt; + + epkt = cp + datalen; + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) + return (0); + m->m_pkthdr.rcvif = &sc->sc_if; + m->m_pkthdr.len = datalen; + m->m_len = MHLEN; + + while (datalen > 0) { + if (top) { + MGET(m, M_DONTWAIT, MT_DATA); + if (m == 0) { + m_freem(top); + return (0); + } + m->m_len = MLEN; + } + len = min(datalen, epkt - cp); + if (len >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if (m->m_flags & M_EXT) + m->m_len = len = min(len, MCLBYTES); + else + len = m->m_len; + } else { + /* + * Place initial small packet/header at end of mbuf. + */ + if (len < m->m_len) { + if (top == 0 && len + max_linkhdr <= m->m_len) + m->m_data += max_linkhdr; + m->m_len = len; + } else + len = m->m_len; + } + bcopy(cp, mtod(m, caddr_t), (unsigned) len); + cp += len; + *mp = m; + mp = &m->m_next; + datalen -= len; + if (cp == epkt) + cp = spkt; + } + return (top); +} diff --git a/sys/arch/wgrisc/dev/if_sn.h b/sys/arch/wgrisc/dev/if_sn.h new file mode 100644 index 00000000000..4139a1c7850 --- /dev/null +++ b/sys/arch/wgrisc/dev/if_sn.h @@ -0,0 +1,347 @@ +/* $OpenBSD: if_sn.h,v 1.1 1997/02/06 16:02:42 pefo Exp $ */ +/* + * Copyright (c) 1991 Algorithmics Ltd (http://www.algor.co.uk) + * You may use, copy, and modify this program so long as you retain the + * copyright line. + */ + +/* + * if_sonic.h -- National Semiconductor DP83932BVF (SONIC) + */ + +/* + * Accessing SONIC data structures and registers as 32 bit values + * makes code endianess independent. The SONIC is however always in + * bigendian mode so it is necessary to ensure that data structures shared + * between the CPU and the SONIC are always in bigendian order. + */ + +/* + * Receive Resource Descriptor + * This structure describes the buffers into which packets + * will be received. Note that more than one packet may be + * packed into a single buffer if constraints permit. + */ +#if SONICDW == 32 +struct RXrsrc { + u_long buff_ptrlo; /* buffer address LO */ + u_long buff_ptrhi; /* buffer address HI */ + u_long buff_wclo; /* buffer size (16bit words) LO */ + u_long buff_wchi; /* buffer size (16bit words) HI */ +}; +#endif + +/* + * Receive Descriptor + * This structure holds information about packets received. + */ +#if SONICDW == 32 +struct RXpkt { + u_long status; /* + receive status */ + u_long byte_count; /* + packet byte count (including FCS) */ + u_long pkt_ptrlo; /* + packet data LO (in RBA) */ + u_long pkt_ptrhi; /* + packet data HI (in RBA) */ + u_long seq_no; /* + RBA sequence numbers */ + u_long rlink; /* link to next receive descriptor */ + u_long in_use; /* + packet available to SONIC */ + u_long pad; /* pad to multiple of 16 bytes */ +}; +#endif +#define RBASEQ(x) (((x)>>8)&0xff) +#define PSNSEQ(x) ((x) & 0xff) + +/* + * Transmit Descriptor + * This structure holds information about packets to be transmitted. + */ +#define FRAGMAX 31 /* maximum number of fragments in a packet */ +#if SONICDW == 32 +struct TXpkt { + u_long status; /* + transmitted packet status */ + u_long config; /* transmission configuration */ + u_long pkt_size; /* entire packet size in bytes */ + u_long frag_count; /* # fragments in packet */ + union { + struct { + u_long _frag_ptrlo; /* pointer to packet fragment LO */ + u_long _frag_ptrhi; /* pointer to packet fragment HI */ + u_long _frag_size; /* fragment size */ + } u_frag; + struct { + u_long _tlink; /* link to next transmit descriptor */ + } u_link; + } u[FRAGMAX+1]; /* +1 makes tcp->u[FRAGMAX].u_link.link valid! */ +}; +#endif + +#define frag_ptrlo u_frag._frag_ptrlo +#define frag_ptrhi u_frag._frag_ptrhi +#define frag_size u_frag._frag_size +#define tlink u_link._tlink + +#define EOL 0x0001 /* end of list marker for link fields */ + +#define MAXCAM 16 /* number of user entries in CAM */ +#if SONICDW == 32 +struct CDA { + struct { + u_long cam_ep; /* CAM Entry Pointer */ + u_long cam_ap0; /* CAM Address Port 0 xx-xx-xx-xx-YY-YY */ + u_long cam_ap1; /* CAM Address Port 1 xx-xx-YY-YY-xxxx */ + u_long cam_ap2; /* CAM Address Port 2 YY-YY-xx-xx-xx-xx */ + } desc[MAXCAM]; + u_long enable; /* mask enabling CAM entries */ +}; +#endif + +/* + * SONIC registers as seen by the processor + */ +struct sonic_reg { + volatile u_long s_cr; /* 00: Command */ + volatile u_long s_dcr; /* 01: Data Configuration */ + volatile u_long s_rcr; /* 02: Receive Control */ + volatile u_long s_tcr; /* 03: Transmit Control */ + volatile u_long s_imr; /* 04: Interrupt Mask */ + volatile u_long s_isr; /* 05: Interrupt Status */ + volatile u_long s_utda; /* 06: Upper Transmit Descriptor Address */ + volatile u_long s_ctda; /* 07: Current Transmit Descriptor Address */ + volatile u_long _s_tps; /* 08* Transmit Packet Size */ + volatile u_long _s_tfc; /* 09* Transmit Fragment Count */ + volatile u_long _s_tsa0; /* 0a* Transmit Start Address 0 */ + volatile u_long _s_tsa1; /* 0b* Transmit Start Address 1 */ + volatile u_long _s_tfs; /* 0c* Transmit Fragment Size */ + volatile u_long s_urda; /* 0d: Upper Receive Descriptor Address */ + volatile u_long s_crda; /* 0e: Current Receive Descriptor Address */ + volatile u_long _s_crba0; /* 0f* Current Receive Buffer Address 0 */ + volatile u_long _s_crba1; /* 10* Current Receive Buffer Address 1 */ + volatile u_long _s_rbwc0; /* 11* Remaining Buffer Word Count 0 */ + volatile u_long _s_rbwc1; /* 12* Remaining Buffer Word Count 1 */ + volatile u_long s_eobc; /* 13: End Of Buffer Word Count */ + volatile u_long s_urra; /* 14: Upper Receive Resource Address */ + volatile u_long s_rsa; /* 15: Resource Start Address */ + volatile u_long s_rea; /* 16: Resource End Address */ + volatile u_long s_rrp; /* 17: Resource Read Pointer */ + volatile u_long s_rwp; /* 18: Resource Write Pointer */ + volatile u_long _s_trba0; /* 19* Temporary Receive Buffer Address 0 */ + volatile u_long _s_trba1; /* 1a* Temporary Receive Buffer Address 1 */ + volatile u_long _s_tbwc0; /* 1b* Temporary Buffer Word Count 0 */ + volatile u_long _s_tbwc1; /* 1c* Temporary Buffer Word Count 1 */ + volatile u_long _s_addr0; /* 1d* Address Generator 0 */ + volatile u_long _s_addr1; /* 1e* Address Generator 1 */ + volatile u_long _s_llfa; /* 1f* Last Link Field Address */ + volatile u_long _s_ttda; /* 20* Temp Transmit Descriptor Address */ + volatile u_long s_cep; /* 21: CAM Entry Pointer */ + volatile u_long s_cap2; /* 22: CAM Address Port 2 */ + volatile u_long s_cap1; /* 23: CAM Address Port 1 */ + volatile u_long s_cap0; /* 24: CAM Address Port 0 */ + volatile u_long s_ce; /* 25: CAM Enable */ + volatile u_long s_cdp; /* 26: CAM Descriptor Pointer */ + volatile u_long s_cdc; /* 27: CAM Descriptor Count */ + volatile u_long s_sr; /* 28: Silicon Revision */ + volatile u_long s_wt0; /* 29: Watchdog Timer 0 */ + volatile u_long s_wt1; /* 2a: Watchdog Timer 1 */ + volatile u_long s_rsc; /* 2b: Receive Sequence Counter */ + volatile u_long s_crct; /* 2c: CRC Error Tally */ + volatile u_long s_faet; /* 2d: FAE Tally */ + volatile u_long s_mpt; /* 2e: Missed Packet Tally */ + volatile u_long _s_mdt; /* 2f* Maximum Deferral Timer */ + volatile u_long _s_rtc; /* 30* Receive Test Control */ + volatile u_long _s_ttc; /* 31* Transmit Test Control */ + volatile u_long _s_dtc; /* 32* DMA Test Control */ + volatile u_long _s_cc0; /* 33* CAM Comparison 0 */ + volatile u_long _s_cc1; /* 34* CAM Comparison 1 */ + volatile u_long _s_cc2; /* 35* CAM Comparison 2 */ + volatile u_long _s_cm; /* 36* CAM Match */ + volatile u_long :32; /* 37* reserved */ + volatile u_long :32; /* 38* reserved */ + volatile u_long _s_rbc; /* 39* Receiver Byte Count */ + volatile u_long :32; /* 3a* reserved */ + volatile u_long _s_tbo; /* 3b* Transmitter Backoff Counter */ + volatile u_long _s_trc; /* 3c* Transmitter Random Counter */ + volatile u_long _s_tbm; /* 3d* Transmitter Backoff Mask */ + volatile u_long :32; /* 3e* Reserved */ + volatile u_long s_dcr2; /* 3f Data Configuration 2 (AVF) */ +}; + +/* + * Register Interpretations + */ + +/* + * The command register is used for issuing commands to the SONIC. + * With the exception of CR_RST, the bit is reset when the operation + * completes. + */ +#define CR_LCAM 0x0200 /* load CAM with descriptor at s_cdp */ +#define CR_RRRA 0x0100 /* read next RRA descriptor at s_rrp */ +#define CR_RST 0x0080 /* software reset */ +#define CR_ST 0x0020 /* start timer */ +#define CR_STP 0x0010 /* stop timer */ +#define CR_RXEN 0x0008 /* receiver enable */ +#define CR_RXDIS 0x0004 /* receiver disable */ +#define CR_TXP 0x0002 /* transmit packets */ +#define CR_HTX 0x0001 /* halt transmission */ + +/* + * The data configuration register establishes the SONIC's bus cycle + * operation. This register can only be accessed when the SONIC is in + * reset mode (s_cr.CR_RST is set.) + */ +#define DCR_EXBUS 0x8000 /* extended bus mode (AVF) */ +#define DCR_LBR 0x2000 /* latched bus retry */ +#define DCR_PO1 0x1000 /* programmable output 1 */ +#define DCR_PO0 0x0800 /* programmable output 0 */ +#define DCR_STERM 0x0400 /* synchronous termination */ +#define DCR_USR1 0x0200 /* reflects USR1 input pin */ +#define DCR_USR0 0x0100 /* reflects USR0 input pin */ +#define DCR_WC1 0x0080 /* wait state control 1 */ +#define DCR_WC0 0x0040 /* wait state control 0 */ +#define DCR_DW 0x0020 /* data width select */ +#define DCR_BMS 0x0010 /* DMA block mode select */ +#define DCR_RFT1 0x0008 /* receive FIFO threshold control 1 */ +#define DCR_RFT0 0x0004 /* receive FIFO threshold control 0 */ +#define DCR_TFT1 0x0002 /* transmit FIFO threshold control 1 */ +#define DCR_TFT0 0x0001 /* transmit FIFO threshold control 0 */ + +/* data configuration register aliases */ +#define DCR_SYNC DCR_STERM /* synchronous (memory cycle 2 clocks) */ +#define DCR_ASYNC 0 /* asynchronous (memory cycle 3 clocks) */ + +#define DCR_WAIT0 0 /* 0 wait states added */ +#define DCR_WAIT1 DCR_WC0 /* 1 wait state added */ +#define DCR_WAIT2 DCR_WC1 /* 2 wait states added */ +#define DCR_WAIT3 (DCR_WC1|DCR_WC0) /* 3 wait states added */ + +#define DCR_DW16 0 /* use 16-bit DMA accesses */ +#define DCR_DW32 DCR_DW /* use 32-bit DMA accesses */ + +#define DCR_DMAEF 0 /* DMA until TX/RX FIFO has emptied/filled */ +#define DCR_DMABLOCK DCR_BMS /* DMA until RX/TX threshold crossed */ + +#define DCR_RFT4 0 /* receive threshold 4 bytes */ +#define DCR_RFT8 DCR_RFT0 /* receive threshold 8 bytes */ +#define DCR_RFT16 DCR_RFT1 /* receive threshold 16 bytes */ +#define DCR_RFT24 (DCR_RFT1|DCR_RFT0) /* receive threshold 24 bytes */ + +#define DCR_TFT8 0 /* transmit threshold 8 bytes */ +#define DCR_TFT16 DCR_TFT0 /* transmit threshold 16 bytes */ +#define DCR_TFT24 DCR_TFT1 /* transmit threshold 24 bytes */ +#define DCR_TFT28 (DCR_TFT1|DCR_TFT0) /* transmit threshold 28 bytes */ + +/* + * The receive control register is used to filter incoming packets and + * provides status information on packets received. + * The contents of the register are copied into the RXpkt.status field + * when a packet is received. RCR_MC - RCR_PRX are then reset. + */ +#define RCR_ERR 0x8000 /* accept packets with CRC errors */ +#define RCR_RNT 0x4000 /* accept runt (length < 64) packets */ +#define RCR_BRD 0x2000 /* accept broadcast packets */ +#define RCR_PRO 0x1000 /* accept all physical address packets */ +#define RCR_AMC 0x0800 /* accept all multicast packets */ +#define RCR_LB1 0x0400 /* loopback control 1 */ +#define RCR_LB0 0x0200 /* loopback control 0 */ +#define RCR_MC 0x0100 /* multicast packet received */ +#define RCR_BC 0x0080 /* broadcast packet received */ +#define RCR_LPKT 0x0040 /* last packet in RBA (RBWC < EOBC) */ +#define RCR_CRS 0x0020 /* carrier sense activity */ +#define RCR_COL 0x0010 /* collision activity */ +#define RCR_CRC 0x0008 /* CRC error */ +#define RCR_FAE 0x0004 /* frame alignment error */ +#define RCR_LBK 0x0002 /* loopback packet received */ +#define RCR_PRX 0x0001 /* packet received without errors */ + +/* receiver control register aliases */ +/* the loopback control bits provide the following options */ +#define RCR_LBNONE 0 /* no loopback - normal operation */ +#define RCR_LBMAC RCR_LB0 /* MAC loopback */ +#define RCR_LBENDEC RCR_LB1 /* ENDEC loopback */ +#define RCR_LBTRANS (RCR_LB1|RCR_LB0) /* transceiver loopback */ + +/* + * The transmit control register controls the SONIC's transmit operations. + * TCR_PINT - TCR_EXDIS are loaded from the TXpkt.config field at the + * start of transmission. TCR_EXD-TCR_PTX are cleared at the beginning + * of transmission and updated when the transmission is completed. + */ +#define TCR_PINT 0x8000 /* interrupt when transmission starts */ +#define TCR_POWC 0x4000 /* program out of window collision timer */ +#define TCR_CRCI 0x2000 /* transmit packet without 4 byte FCS */ +#define TCR_EXDIS 0x1000 /* disable excessive deferral timer */ +#define TCR_EXD 0x0400 /* excessive deferrals occurred (>3.2ms) */ +#define TCR_DEF 0x0200 /* deferred transmissions occurred */ +#define TCR_NCRS 0x0100 /* carrier not present during transmission */ +#define TCR_CRSL 0x0080 /* carrier lost during transmission */ +#define TCR_EXC 0x0040 /* excessive collisions (>16) detected */ +#define TCR_OWC 0x0020 /* out of window (bad) collision occurred */ +#define TCR_PMB 0x0008 /* packet monitored bad - the tansmitted + * packet had a bad source address or CRC */ +#define TCR_FU 0x0004 /* FIFO underrun (memory access failed) */ +#define TCR_BCM 0x0002 /* byte count mismatch (TXpkt.pkt_size + * != sum(TXpkt.frag_size) */ +#define TCR_PTX 0x0001 /* packet transmitted without errors */ + +/* transmit control register aliases */ +#define TCR_OWCSFD 0 /* start after start of frame delimiter */ +#define TCR_OWCPRE TCR_POWC /* start after first bit of preamble */ + + +/* + * The interrupt mask register masks the interrupts that + * are generated from the interrupt status register. + * All reserved bits should be written with 0. + */ +#define IMR_BREN 0x4000 /* bus retry occurred enable */ +#define IMR_HBLEN 0x2000 /* heartbeat lost enable */ +#define IMR_LCDEN 0x1000 /* load CAM done interrupt enable */ +#define IMR_PINTEN 0x0800 /* programmable interrupt enable */ +#define IMR_PRXEN 0x0400 /* packet received enable */ +#define IMR_PTXEN 0x0200 /* packet transmitted enable */ +#define IMR_TXEREN 0x0100 /* transmit error enable */ +#define IMR_TCEN 0x0080 /* timer complete enable */ +#define IMR_RDEEN 0x0040 /* receive descriptors exhausted enable */ +#define IMR_RBEEN 0x0020 /* receive buffers exhausted enable */ +#define IMR_RBAEEN 0x0010 /* receive buffer area exceeded enable */ +#define IMR_CRCEN 0x0008 /* CRC tally counter rollover enable */ +#define IMR_FAEEN 0x0004 /* FAE tally counter rollover enable */ +#define IMR_MPEN 0x0002 /* MP tally counter rollover enable */ +#define IMR_RFOEN 0x0001 /* receive FIFO overrun enable */ + + +/* + * The interrupt status register indicates the source of an interrupt when + * the INT pin goes active. The interrupt is acknowledged by writing + * the appropriate bit(s) in this register. + */ +#define ISR_ALL 0xffff /* all interrupts */ +#define ISR_BR 0x4000 /* bus retry occurred */ +#define ISR_HBL 0x2000 /* CD heartbeat lost */ +#define ISR_LCD 0x1000 /* load CAM command has completed */ +#define ISR_PINT 0x0800 /* programmed interrupt from TXpkt.config */ +#define ISR_PKTRX 0x0400 /* packet received */ +#define ISR_TXDN 0x0200 /* no remaining packets to be transmitted */ +#define ISR_TXER 0x0100 /* packet transmission caused error */ +#define ISR_TC 0x0080 /* timer complete */ +#define ISR_RDE 0x0040 /* receive descriptors exhausted */ +#define ISR_RBE 0x0020 /* receive buffers exhausted */ +#define ISR_RBAE 0x0010 /* receive buffer area exceeded */ +#define ISR_CRC 0x0008 /* CRC tally counter rollover */ +#define ISR_FAE 0x0004 /* FAE tally counter rollover */ +#define ISR_MP 0x0002 /* MP tally counter rollover */ +#define ISR_RFO 0x0001 /* receive FIFO overrun */ + +/* + * The second data configuration register allows additional user defined + * pins to be controlled. These bits are only available if s_dcr.DCR_EXBUS + * is set. + */ +#define DCR2_EXPO3 0x8000 /* EXUSR3 output */ +#define DCR2_EXPO2 0x4000 /* EXUSR2 output */ +#define DCR2_EXPO1 0x2000 /* EXUSR1 output */ +#define DCR2_EXPO0 0x1000 /* EXUSR0 output */ +#define DCR2_PHL 0x0010 /* extend HOLD signal by 1/2 clock */ +#define DCR2_LRDY 0x0008 /* set latched ready mode */ +#define DCR2_PCM 0x0004 /* packet compress on match */ +#define DCR2_PCNM 0x0002 /* packet compress on mismatch */ +#define DCR2_RJM 0x0001 /* reject on match */ diff --git a/sys/arch/wgrisc/dev/scsi.h b/sys/arch/wgrisc/dev/scsi.h new file mode 100644 index 00000000000..79f6886db9f --- /dev/null +++ b/sys/arch/wgrisc/dev/scsi.h @@ -0,0 +1,559 @@ +/* $OpenBSD: scsi.h,v 1.1 1997/02/06 16:02:41 pefo Exp $ */ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell. + * + * 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. + * + * from: @(#)scsi.h 8.1 (Berkeley) 6/10/93 + * $Id: scsi.h,v 1.1 1997/02/06 16:02:41 pefo Exp $ + * + * scsi.h -- + * + * Common declarations for SCSI command formaters. This file only covers + * definitions pertaining to the SCSI common command set that are + * common to all SCSI device types (ie disk, tapes, WORM, printers, etc). + * Some of the references from the proceedings of the + * 1984 Mini/Micro Northeast conference might help in understanding SCSI. + * + * from: Header: /sprite/src/kernel/dev/RCS/scsi.h, + * v 9.1 90/02/13 23:11:24 jhh Exp SPRITE (Berkeley) + * $Id: scsi.h,v 1.1 1997/02/06 16:02:41 pefo Exp $ + */ + +#ifndef _SCSI_H +#define _SCSI_H + +/* + * "Standard" SCSI Commands. + * SCSI commands are divided into 8 groups as follows: + * Group0 (0x00 - 0x1f). Basic commands. 6 bytes long + * Group1 (0x20 - 0x3f). Extended command. 10 bytes. + * Group2 (0x40 - 0x5f). Reserved. + * Group2 (0x60 - 0x7f). Reserved. + * Group2 (0x80 - 0x9f). Reserved. + * Group2 (0xa0 - 0xbf). Reserved. + * Group6 (0xc0 - 0xdf). Vendor Unique + * Group7 (0xe0 - 0xff). Vendor Unique + */ + +/* + * Scsi Group0 commands all are 6 bytes and have a format according to + * struct ScsiGroup0Cmd. + */ +#define SCSI_TEST_UNIT_READY 0x00 +#define SCSI_REZERO_UNIT 0x01 +#define SCSI_REWIND 0x01 +#define SCSI_REQUEST_SENSE 0x03 +#define SCSI_FORMAT_UNIT 0x04 +#define SCSI_READ_BLOCK_LIMITS 0x05 +#define SCSI_REASSIGN_BLOCKS 0x07 +#define SCSI_READ 0x08 +#define SCSI_WRITE 0x0a +#define SCSI_SEEK 0x0b +#define SCSI_TRACK_SELECT 0x0b +#define SCSI_READ_REVERSE 0x0f +#define SCSI_WRITE_EOF 0x10 +#define SCSI_SPACE 0x11 +#define SCSI_INQUIRY 0x12 +#define SCSI_VERIFY 0x13 +#define SCSI_READ_BUFFER 0x14 +#define SCSI_MODE_SELECT 0x15 +#define SCSI_RESERVE_UNIT 0x16 +#define SCSI_RELEASE_UNIT 0x17 +#define SCSI_COPY 0x18 +#define SCSI_ERASE_TAPE 0x19 +#define SCSI_MODE_SENSE 0x1a +#define SCSI_START_STOP 0x1b +#define SCSI_LOAD_UNLOAD 0x1b +#define SCSI_RECV_DIAG_RESULTS 0x1c +#define SCSI_SEND_DIAGNOSTIC 0x1d +#define SCSI_PREVENT_ALLOW 0x1e + +/* + * Group1 commands are all 10 bytes and have a format according to + * struct ScsiGroup1Cmd. + */ +#define SCSI_READ_CAPACITY 0x25 +#define SCSI_READ_EXT 0x28 +#define SCSI_WRITE_EXT 0x2a +#define SCSI_SEEK_EXT 0x2b +#define SCSI_WRITE_VERIFY 0x2e +#define SCSI_VERIFY_EXT 0x2f +#define SCSI_SEARCH_HIGH 0x30 +#define SCSI_SEARCH_EQUAL 0x31 +#define SCSI_SEARCH_LOW 0x32 +#define SCSI_SET_LIMITS 0x33 +#define SCSI_COMPARE 0x39 +#define SCSI_COPY_VERIFY 0x3a + +/* + * Control byte flags for Group0 and Group1 commands. + * + * SCSI_CTRL_LINK - This is used to prevent a bus free phase between commands. + * If the command terminates successfully, a SCSI_LINKED_CMD_COMPLETE + * message is returned instead of the normal SCSI_COMMAND_COMPLETE message. * The last command in a chain should not have this bit set + * (and consequently gets a normal SCSI_COMMAND_COMPLETE message). + * SCSI_CTRL_LINK_FLAG - This bit should only set when SCSI_CTRL_LINK is set and + * causes a SCSI_LINKED_FLAGED_CMD_COMPLETE to be returned instead of + * a SCSI_LINKED_CMD_COMPLETE. + */ +#define SCSI_CTRL_LINK 0x01 /* Link commands (no bus free phase) */ +#define SCSI_CTRL_LINK_INTR 0x02 /* Interrupt after linked command */ + +/* + * The standard group0 6-byte SCSI control block. Note that the + * fields between highAddr and blockCount inclusive are command dependent. + * The definitions Addr and BlockCount cover most of the commands we will + * use. + */ +typedef struct ScsiGroup0Cmd { + u_char command; /* command code, defined below. The + * upper three bits of this are zero + * to indicate the control block is + * only 6 bytes long */ +#if BYTE_ORDER == BIG_ENDIAN + u_char unitNumber :3; /* Logical Unit (LUN) to which to + * pass the command. The device + * has already been selected using + * the "targetID" bit. */ + u_char highAddr :5; /* High bits of address */ +#else + u_char highAddr :5; /* High bits of address */ + u_char unitNumber :3; /* Logical Unit (LUN) to which to + * pass the command. The device + * has already been selected using + * the "targetID" bit. */ +#endif + u_char midAddr; /* Middle bits of address */ + u_char lowAddr; /* Low bits of address */ + u_char blockCount; /* Blocks to transfer */ + u_char control; /* See flags for common bits */ +} ScsiGroup0Cmd; + +/* + * Format of a SCSI_START_STOP command. This is a group 0 command, but + * the command contents are different. + */ +typedef struct ScsiStartStopCmd { +#if BYTE_ORDER == BIG_ENDIAN + u_char command; /* command code, defined below. The + * upper three bits of this are zero + * to indicate the control block is + * only 6 bytes long */ + u_char unitNumber :3; /* Logical Unit (LUN) to which to + * pass the command. The device + * has already been selected using + * the "targetID" bit. */ + u_char pad1 :4; /* Reserved */ + u_char immed :1; /* Immediate status bit */ + u_char pad2; /* Reserved */ + u_char pad3; /* Reserved */ + u_char pad4 :6; /* Reserved */ + u_char loadEject :1; /* Load or eject medium */ + u_char start :1; /* Start or stop medium */ + u_char control; /* See flags for common bits */ +#else + u_char command; /* command code, defined below. The + * upper three bits of this are zero + * to indicate the control block is + * only 6 bytes long */ + u_char immed :1; /* Immediate status bit */ + u_char pad1 :4; /* Reserved */ + u_char unitNumber :3; /* Logical Unit (LUN) to which to + * pass the command. The device + * has already been selected using + * the "targetID" bit. */ + u_char pad2; /* Reserved */ + u_char pad3; /* Reserved */ + u_char start :1; /* Start or stop medium */ + u_char loadEject :1; /* Load or eject medium */ + u_char pad4 :6; /* Reserved */ + u_char control; /* See flags for common bits */ +#endif +} ScsiStartStopCmd; + +/* + * The standard group1 10-byte SCSI control block. Note that the + * fields between highAddr and blockCount inclusive are command dependent. + * The definitions Addr and BlockCount cover most of the commands we will + * use. + */ +typedef struct ScsiGroup1Cmd { + u_char command; /* command code, defined below. The + * upper three bits of this are zero + * to indicate the control block is + * only 6 bytes long */ +#if BYTE_ORDER == BIG_ENDIAN + u_char unitNumber :3; /* Logical Unit (LUN) to which to + * pass the command. The device + * has already been selected using + * the "targetID" bit. */ + u_char pad1 :5; /* Reserved */ +#else + u_char pad1 :5; /* Reserved */ + u_char unitNumber :3; /* Logical Unit (LUN) to which to + * pass the command. The device + * has already been selected using + * the "targetID" bit. */ +#endif + u_char highAddr; /* High bits of address */ + u_char midHighAddr; /* Middle high bits of address */ + u_char midLowAddr; /* Middle low bits of address */ + u_char lowAddr; /* Low bits of address */ + u_char pad2; /* Reserved */ + u_char highBlockCount; /* High bits of blocks to transfer */ + u_char lowBlockCount; /* Low bits of blocks to transfer */ + u_char control; /* See flags for common bits */ +} ScsiGroup1Cmd; + +/* + * SCSI status completion information. + * This is returned by the device when a command completes. + */ +#define SCSI_STATUS_CHECKCOND 0x02 /* Check Condition (ie., read sense) */ +#define SCSI_STATUS_CONDMET 0x04 /* Condition Met (ie., search worked) */ +#define SCSI_STATUS_BUSY 0x08 +#define SCSI_STATUS_INTERMED 0x10 /* Intermediate status sent */ +#define SCSI_STATUS_EXT 0x80 /* Extended status valid */ + +/* + * Sense information provided after some errors. This is divided into + * two kinds, classes 0-6, and class 7. This is 30 bytes big to allow + * for the drive specific sense bytes that follow the standard 4 byte header. + * + * For extended sense, this buffer may be cast into another type. Also + * The actual size of the sense data returned is used to detect what + * kind of tape drive is out there. Kludgy, but true. + */ +typedef struct ScsiClass0Sense { +#if BYTE_ORDER == BIG_ENDIAN + u_char valid :1; /* Sense data is valid */ + u_char error :7; /* 3 bits class and 4 bits code */ +#else + u_char error :7; /* 3 bits class and 4 bits code */ + u_char valid :1; /* Sense data is valid */ +#endif + u_char highAddr; /* High byte of block address */ + u_char midAddr; /* Middle byte of block address */ + u_char lowAddr; /* Low byte of block address */ + u_char sense[26]; /* Target specific sense data */ +} ScsiClass0Sense; + +/* + * Definitions for errors in the sense data. The error field is specified + * as a 3 bit class and 4 bit code, but it is easier to treat it as a + * single 7 bit field. + */ +#define SCSI_NO_SENSE_DATA 0x00 +#define SCSI_NOT_READY 0x04 +#define SCSI_NOT_LOADED 0x09 +#define SCSI_INSUF_CAPACITY 0x0a +#define SCSI_HARD_DATA_ERROR 0x11 +#define SCSI_WRITE_PROTECT 0x17 +#define SCSI_CORRECTABLE_ERROR 0x18 +#define SCSI_FILE_MARK 0x1c +#define SCSI_INVALID_COMMAND 0x20 +#define SCSI_UNIT_ATTENTION 0x30 +#define SCSI_END_OF_MEDIA 0x34 + +/* + * The standard "extended" sense data returned by SCSI devices. This + * has an error field of 0x70, for a "class 7" error. + */ +typedef struct ScsiClass7Sense { +#if BYTE_ORDER == BIG_ENDIAN + u_char valid :1; /* Sense data is valid */ + u_char error7 :7; /* == 0x70 */ + u_char pad1; /* Also "segment number" for copy */ + u_char fileMark :1; /* File mark on device */ + u_char endOfMedia :1; /* End of media reached */ + u_char badBlockLen :1; /* Block length mis-match (Exabyte) */ + u_char pad2 :1; + u_char key :4; /* Sense keys defined below */ + u_char info1; /* Information byte 1 */ + u_char info2; /* Information byte 2 */ + u_char info3; /* Information byte 3 */ + u_char info4; /* Information byte 4 */ + u_char length; /* Number of additional info bytes */ +#else + u_char error7 :7; /* == 0x70 */ + u_char valid :1; /* Sense data is valid */ + u_char pad1; /* Also "segment number" for copy */ + u_char key :4; /* Sense keys defined below */ + u_char pad2 :1; + u_char badBlockLen :1; /* Block length mis-match (Exabyte) */ + u_char endOfMedia :1; /* End of media reached */ + u_char fileMark :1; /* File mark on device */ + u_char info1; /* Information byte 1 */ + u_char info2; /* Information byte 2 */ + u_char info3; /* Information byte 3 */ + u_char info4; /* Information byte 4 */ + u_char length; /* Number of additional info bytes */ +#endif +} ScsiClass7Sense; /* 8 Bytes */ + +/* + * Key values for standardized sense class 7. + */ +#define SCSI_CLASS7_NO_SENSE 0 +#define SCSI_CLASS7_RECOVERABLE 1 +#define SCSI_CLASS7_NOT_READY 2 +#define SCSI_CLASS7_MEDIA_ERROR 3 +#define SCSI_CLASS7_HARDWARE_ERROR 4 +#define SCSI_CLASS7_ILLEGAL_REQUEST 5 + +/* + * These seem to have different meanings to different vendors.... + */ +#define SCSI_CLASS7_MEDIA_CHANGE 6 +#define SCSI_CLASS7_UNIT_ATTN 6 + +#define SCSI_CLASS7_WRITE_PROTECT 7 +#define SCSI_CLASS7_BLANK_CHECK 8 +#define SCSI_CLASS7_VENDOR 9 +#define SCSI_CLASS7_POWER_UP_FAILURE 10 +#define SCSI_CLASS7_ABORT 11 +#define SCSI_CLASS7_EQUAL 12 +#define SCSI_CLASS7_OVERFLOW 13 +#define SCSI_CLASS7_RESERVED_14 14 +#define SCSI_CLASS7_RESERVED_15 15 + +/* + * Data return by the SCSI inquiry command. + */ +typedef struct ScsiInquiryData { +#if BYTE_ORDER == BIG_ENDIAN + u_char type; /* Peripheral Device type. See below. */ + u_char rmb:1; /* Removable Medium bit. */ + u_char qualifier:7; /* Device type qualifier. */ + u_char version; /* Version info. */ + u_char reserved:4; /* reserved. */ + u_char format:4; /* Response format. */ + u_char length; /* length of data returned. */ + u_char reserved2[2]; /* Reserved */ + u_char flags; /* SCSI II flags (see below) */ + u_char vendorID[8]; /* Vendor ID (ASCII) */ + u_char productID[16]; /* Product ID (ASCII) */ + u_char revLevel[4]; /* Revision level (ASCII) */ + u_char revData[8]; /* Revision data (ASCII) */ +#else + u_char type; /* Peripheral Device type. See below. */ + u_char qualifier:7; /* Device type qualifier. */ + u_char rmb:1; /* Removable Medium bit. */ + u_char version; /* Version info. */ + u_char format:4; /* Response format. */ + u_char reserved:4; /* reserved. */ + u_char length; /* length of data returned. */ + u_char reserved2[2]; /* Reserved */ + u_char flags; /* SCSI II flags (see below) */ + u_char vendorID[8]; /* Vendor ID (ASCII) */ + u_char productID[16]; /* Product ID (ASCII) */ + u_char revLevel[4]; /* Revision level (ASCII) */ + u_char revData[8]; /* Revision data (ASCII) */ +#endif +} ScsiInquiryData; + +/* + * The SCSI Peripheral type ID codes as return by the SCSI_INQUIRY command. + * + * SCSI_DISK_TYPE - Direct Access Device. + * SCSI_TAPE_TYPE - Sequential Access Device. + * SCSI_PRINTER_TYPE - Printer Device. + * SCSI_HOST_TYPE - Processor Device. + * SCSI_WORM_TYPE - Write-Once Read-Multiple Device. + * SCSI_ROM_TYPE - Read-Only Direct Access Device. + * SCSI_SCANNER_TYPE - Scanner device. + * SCSI_OPTICAL_MEM_TYPE - Optical memory device. + * SCSI_MEDIUM_CHANGER_TYPE - Medium changer device. + * SCSI_COMMUNICATIONS_TYPE - Communications device. + * SCSI_NODEVICE_TYPE - Logical Unit not present or implemented. + * + * Note that codes 0xa-0x7e are reserved and 0x80-0xff are vendor unique. + */ +#define SCSI_DISK_TYPE 0 +#define SCSI_TAPE_TYPE 1 +#define SCSI_PRINTER_TYPE 2 +#define SCSI_HOST_TYPE 3 +#define SCSI_WORM_TYPE 4 +#define SCSI_ROM_TYPE 5 +#define SCSI_SCANNER_TYPE 6 +#define SCSI_OPTICAL_MEM_TYPE 7 +#define SCSI_MEDIUM_CHANGER_TYPE 8 +#define SCSI_COMMUNICATIONS_TYPE 9 +#define SCSI_NODEVICE_TYPE 0x7f + +/* + * The SCSI I & II inquiry flags. + * + * SCSI_REL_ADR - Relative addressing supported. + * SCSI_WIDE_32 - 32 bit wide SCSI bus transfers supported. + * SCSI_WIDE_16 - 16 bit wide SCSI bus transfers supported. + * SCSI_SYNC - Synchronous data transfers supported. + * SCSI_LINKED - Linked commands supported. + * SCSI_CMD_QUEUE - Tagged command queuing supported. + * SCSI_SOFT_RESET - Soft RESET alternative suported. + */ +#define SCSI_REL_ADR 0x80 +#define SCSI_WIDE_32 0x40 +#define SCSI_WIDE_16 0x20 +#define SCSI_SYNC 0x10 +#define SCSI_LINKED 0x08 +#define SCSI_CMD_QUEUE 0x02 +#define SCSI_SOFT_RESET 0x01 + +/* + * Standard header for SCSI_MODE_SENSE and SCSI_MODE_SELECT commands for tapes. + */ +typedef struct ScsiTapeModeSelectHdr { + u_char len; /* length */ + u_char media; /* media type */ +#if BYTE_ORDER == BIG_ENDIAN + u_char writeprot:1; /* Write protected media */ + u_char bufferedMode:3; /* Type of buffer to be done. */ + u_char speed:4; /* Drive speed. */ +#else + u_char speed:4; /* Drive speed. */ + u_char bufferedMode:3; /* Type of buffer to be done. */ + u_char writeprot:1; /* Write protected media */ +#endif + u_char length; /* Block descriptor length. */ + u_char density; /* tape density code */ + u_char blocks_2; /* number of blocks (MSB) */ + u_char blocks_1; /* number of blocks */ + u_char blocks_0; /* number of blocks (LSB) */ + u_char reserved; /* */ + u_char block_size2; /* Tape block size (MSB) */ + u_char block_size1; /* Tape block size */ + u_char block_size0; /* Tape block size (LSB) */ + u_char vendor[6]; /* vendor specific data */ +} ScsiTapeModeSelectHdr; + +/* + * Definitions of SCSI messages. + * + * SCSI_COMMAND_COMPLETE - After a command has completed, successfully + * or not, this is returned to the host from the target. + * + * SCSI_EXTENDED_MSG - Indicates that a multi-byte message is being sent. + * + * The following messages are used with connect/disconnect: + * SCSI_SAVE_DATA_POINTER - Sent from target to host to request saving + * of current DMA address and count. Indicates a pending dis-connect. + * SCSI_RESTORE_POINTER - Sent from the target to the host to request + * restoring pointers saved before a disconnect + * SCSI_DISCONNECT - Sent from the target to the host to disconnect. + * SCSI_ABORT - Sent from the host to the target to abort current request. + * SCSI_MESSAGE_REJECT - Indicates receipt, by either host or target, of + * an unimplemented message. + * SCSI_NO_OP - Sent from host to target if it has no real message to send. + * SCSI_MESSAGE_PARITY_ERROR - Sent from host to target on message parity error + * SCSI_BUS_RESET - Sent from host to target to reset all current I/O + * + * SCSI_IDENTIFY - The low order two bits of this message type indicate + * the Logical Unit of the Target which is requesting a reconnect. + * SCSI_DIS_REC_IDENTIFY - Sent from the host to a target to indicate + * is supports connect/dis-connect + * + */ +#define SCSI_COMMAND_COMPLETE 0x00 +#define SCSI_EXTENDED_MSG 0x01 +#define SCSI_SAVE_DATA_POINTER 0x02 +#define SCSI_RESTORE_POINTERS 0x03 +#define SCSI_DISCONNECT 0x04 +#define SCSI_ABORT 0x06 +#define SCSI_MESSAGE_REJECT 0x07 +#define SCSI_NO_OP 0x08 +#define SCSI_MESSAGE_PARITY_ERROR 0x09 +#define SCSI_LINKED_CMD_COMPLETE 0x0A +#define SCSI_LINKED_FLAGED_CMD_COMPLETE 0x0B +#define SCSI_BUS_RESET 0x0C + +#define SCSI_IDENTIFY 0x80 +#define SCSI_DIS_REC_IDENTIFY 0xc0 + +/* + * Extended message types (2nd byte of SCSI_EXTENDED_MSG). + */ +#define SCSI_MODIFY_DATA_PTR 0x00 +#define SCSI_SYNCHRONOUS_XFER 0x01 +#define SCSI_EXTENDED_IDENTIFY 0x02 /* only in SCSI I */ +#define SCSI_WIDE_XFER 0x03 + +/* + * Driver ioctl's for various scsi operations. + */ +#ifndef _IOCTL_ +#include <sys/ioctl.h> +#endif + +/* + * Control for SCSI "format" mode. + * + * "Format" mode allows a privileged process to issue direct SCSI + * commands to a drive (it is intended primarily to allow on-line + * formatting). SDIOCSFORMAT with a non-zero arg will put the drive + * into format mode; a zero arg will take it out. When in format + * mode, only the process that issued the SDIOCFORMAT can read or + * write the drive. + * + * In format mode, process is expected to + * - do SDIOCSCSICOMMAND to supply cdb for next SCSI op + * - do read or write as appropriate for cdb + * - if i/o error, optionally do SDIOCSENSE to get completion + * status and sense data from last scsi operation. + */ + +struct scsi_fmt_cdb { + int len; /* cdb length (in bytes) */ + u_char cdb[28]; /* cdb to use on next read/write */ +}; + +struct scsi_fmt_sense { + u_int status; /* completion status of last op */ + u_char sense[32]; /* sense data (if any) from last op */ +}; + +#define SDIOCSFORMAT _IOW('S', 0x1, int) +#define SDIOCGFORMAT _IOR('S', 0x2, int) +#define SDIOCSCSICOMMAND _IOW('S', 0x3, struct scsi_fmt_cdb) +#define SDIOCSENSE _IOR('S', 0x4, struct scsi_fmt_sense) + +#ifdef _KERNEL +/* + * Routines. + */ +extern void scsiGroup0Cmd(); +extern void scsiGroup1Cmd(); +#endif /* _KERNEL */ + +#endif /* _SCSI_H */ |