diff options
author | briggs <briggs@cvs.openbsd.org> | 1996-05-26 19:02:15 +0000 |
---|---|---|
committer | briggs <briggs@cvs.openbsd.org> | 1996-05-26 19:02:15 +0000 |
commit | 4e2e9714c65149d74c0d51f529869cbb80f91351 (patch) | |
tree | 6739b24d2cf272fa63690366f755421c3246eab6 /sys/arch/mac68k | |
parent | 4d20277b0fb5051e3e3ee1ced5ba6ebb190ad69f (diff) |
Missed a few files in the previous updates.
Diffstat (limited to 'sys/arch/mac68k')
-rw-r--r-- | sys/arch/mac68k/dev/ascvar.h | 36 | ||||
-rw-r--r-- | sys/arch/mac68k/dev/grf_subr.c | 85 | ||||
-rw-r--r-- | sys/arch/mac68k/dev/itevar.h | 55 | ||||
-rw-r--r-- | sys/arch/mac68k/dev/ncr5380var.h | 79 | ||||
-rw-r--r-- | sys/arch/mac68k/dev/obio.c | 74 | ||||
-rw-r--r-- | sys/arch/mac68k/dev/sbc.c | 1087 | ||||
-rw-r--r-- | sys/arch/mac68k/dev/sbcreg.h | 28 | ||||
-rw-r--r-- | sys/arch/mac68k/dev/z8530sc.c | 396 | ||||
-rw-r--r-- | sys/arch/mac68k/dev/z8530sc.h | 174 | ||||
-rw-r--r-- | sys/arch/mac68k/dev/z8530tty.c | 1433 | ||||
-rw-r--r-- | sys/arch/mac68k/dev/z8530tty.h | 143 | ||||
-rw-r--r-- | sys/arch/mac68k/dev/zs.c | 752 |
12 files changed, 4342 insertions, 0 deletions
diff --git a/sys/arch/mac68k/dev/ascvar.h b/sys/arch/mac68k/dev/ascvar.h new file mode 100644 index 00000000000..4863e392a9b --- /dev/null +++ b/sys/arch/mac68k/dev/ascvar.h @@ -0,0 +1,36 @@ +/* $OpenBSD: ascvar.h,v 1.1 1996/05/26 19:02:05 briggs Exp $ */ +/* $NetBSD: ascvar.h,v 1.1 1996/05/05 06:16:28 briggs Exp $ */ + +/* + * Copyright (c) 1995 Allen Briggs. 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 Allen Briggs. + * 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. + */ + +int asc_setbellparams __P((int freq, int length, int volume)); +int asc_getbellparams __P((int *freq, int *length, int *volume)); +void asc_bellstop __P((int param)); +int asc_ringbell __P((void)); diff --git a/sys/arch/mac68k/dev/grf_subr.c b/sys/arch/mac68k/dev/grf_subr.c new file mode 100644 index 00000000000..872a41eaca5 --- /dev/null +++ b/sys/arch/mac68k/dev/grf_subr.c @@ -0,0 +1,85 @@ +/* $OpenBSD: grf_subr.c,v 1.1 1996/05/26 19:02:06 briggs Exp $ */ +/* $NetBSD: grf_subr.c,v 1.1 1996/05/19 22:27:08 scottr Exp $ */ + +/*- + * Copyright (c) 1996 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 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. + */ + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/systm.h> + +#include <machine/grfioctl.h> + +#include <mac68k/dev/nubus.h> +#include <mac68k/dev/grfvar.h> + +void +grf_establish(sc, g_mode, g_phys) + struct grfbus_softc *sc; + int (*g_mode) __P((struct grf_softc *, int, void *)); + caddr_t (*g_phys) __P((struct grf_softc *, vm_offset_t)); +{ + struct grfmode *gm = &sc->curr_mode; + struct grfbus_attach_args ga; + + /* Print hardware characteristics. */ + printf("%s: %d x %d, ", sc->sc_dev.dv_xname, gm->width, gm->height); + if (gm->psize == 1) + printf("monochrome\n"); + else + printf("%d color\n", 1 << gm->psize); + + /* Attach grf semantics to the hardware. */ + ga.ga_name = "grf"; + ga.ga_grfmode = gm; + ga.ga_slot = &sc->sc_slot; /* XXX */ + ga.ga_mode = g_mode; + ga.ga_phys = g_phys; + (void)config_found(&sc->sc_dev, &ga, grfbusprint); +} + +int +grfbusprint(aux, name) + void *aux; + char *name; +{ + struct grfbus_attach_args *ga = aux; + + if (name) + printf("%s at %s", ga->ga_name, name); + + return (UNCONF); +} diff --git a/sys/arch/mac68k/dev/itevar.h b/sys/arch/mac68k/dev/itevar.h new file mode 100644 index 00000000000..b6faa91b098 --- /dev/null +++ b/sys/arch/mac68k/dev/itevar.h @@ -0,0 +1,55 @@ +/* $OpenBSD: itevar.h,v 1.1 1996/05/26 19:02:07 briggs Exp $ */ +/* $NetBSD: itevar.h,v 1.1 1996/05/05 06:16:49 briggs Exp $ */ + +/* + * Copyright (c) 1995 Allen Briggs. 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 Allen Briggs. + * 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. + */ + +#include <machine/adbsys.h> + +int ite_intr __P((adb_event_t *event)); +int iteon __P((dev_t dev, int flags)); +int iteoff __P((dev_t dev, int flags)); + +#ifndef CN_DEAD +#include <dev/cons.h> +#endif + +void itestop __P((struct tty * tp, int flag)); +void itestart __P((register struct tty * tp)); +int iteopen __P((dev_t dev, int mode, int devtype, struct proc * p)); +int iteclose __P((dev_t dev, int flag, int mode, struct proc * p)); +int iteread __P((dev_t dev, struct uio * uio, int flag)); +int itewrite __P((dev_t dev, struct uio * uio, int flag)); +int iteioctl __P((dev_t, int, caddr_t, int, struct proc *)); +struct tty *itetty __P((dev_t dev)); + +int itecnprobe __P((struct consdev * cp)); +int itecninit __P((struct consdev * cp)); +int itecngetc __P((dev_t dev)); +int itecnputc __P((dev_t dev, int c)); diff --git a/sys/arch/mac68k/dev/ncr5380var.h b/sys/arch/mac68k/dev/ncr5380var.h new file mode 100644 index 00000000000..3be2849859a --- /dev/null +++ b/sys/arch/mac68k/dev/ncr5380var.h @@ -0,0 +1,79 @@ +/* $OpenBSD: ncr5380var.h,v 1.1 1996/05/26 19:02:08 briggs Exp $ */ +/* $NetBSD: ncr5380var.h,v 1.2 1996/05/25 16:42:31 briggs Exp $ */ + +/* + * Copyright (c) 1995 Allen Briggs. 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 Allen Briggs. + * 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. + */ + +static volatile u_char *scsi_enable = NULL; +static volatile u_char *scsi_flag = NULL; + +static __inline__ void +scsi_clear_drq __P((void)) +{ + int s; + + s = splhigh(); + *scsi_flag = 0x80 | V2IF_SCSIDRQ; + splx(s); +} + +static __inline__ void +scsi_clear_irq __P((void)) +{ + int s; + + s = splhigh(); + *scsi_flag = 0x80 | V2IF_SCSIIRQ; + splx(s); +} + +static __inline__ void +scsi_ienable __P((void)) +{ + int s; + + s = splhigh(); + *scsi_enable = 0x80 | (V2IF_SCSIIRQ | V2IF_SCSIDRQ); + splx(s); +} + +static __inline__ void +scsi_idisable __P((void)) +{ + int s; + + s = splhigh(); + *scsi_enable = V2IF_SCSIIRQ | V2IF_SCSIDRQ; + splx(s); +} + +void pdma_stat __P((void)); +void pdma_cleanup __P((void)); +void scsi_show __P((void)); + diff --git a/sys/arch/mac68k/dev/obio.c b/sys/arch/mac68k/dev/obio.c new file mode 100644 index 00000000000..5603bae9537 --- /dev/null +++ b/sys/arch/mac68k/dev/obio.c @@ -0,0 +1,74 @@ +/* $OpenBSD: obio.c,v 1.1 1996/05/26 19:02:08 briggs Exp $ */ +/* $NetBSD: obio.c,v 1.1 1996/05/05 06:17:07 briggs Exp $ */ + +/* + * Copyright (c) 1994 Gordon W. Ross + * Copyright (c) 1993 Adam Glass + * 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 Adam Glass and Gordon Ross. + * 4. The name of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <machine/autoconf.h> +#include <machine/pte.h> + +static int obio_match __P((struct device *, void *, void *)); +static void obio_attach __P((struct device *, struct device *, void *)); + +struct cfattach obio_ca = { + sizeof(struct device), obio_match, obio_attach +}; + +struct cfdriver obio_cd = { + NULL, "obio", DV_DULL +}; + +static int +obio_match(parent, vcf, aux) + struct device *parent; + void *vcf, *aux; +{ + struct confargs *ca = aux; + + if (ca->ca_bustype != BUS_OBIO) + return (0); + return(1); +} + +static void +obio_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + printf("\n"); + + (void) config_search(bus_scan, self, aux); +} diff --git a/sys/arch/mac68k/dev/sbc.c b/sys/arch/mac68k/dev/sbc.c new file mode 100644 index 00000000000..b46460122dd --- /dev/null +++ b/sys/arch/mac68k/dev/sbc.c @@ -0,0 +1,1087 @@ +/* $OpenBSD: sbc.c,v 1.1 1996/05/26 19:02:09 briggs Exp $ */ +/* $NetBSD: sbc.c,v 1.6 1996/05/08 03:44:56 scottr Exp $ */ + +/* + * Copyright (c) 1996 Scott Reynolds + * Copyright (c) 1995 David Jones + * Copyright (c) 1995 Allen Briggs + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * 4. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by David Jones, Allen + * Briggs and Scott Reynolds. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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. + */ + +/* + * This file contains only the machine-dependent parts of the mac68k + * NCR 5380 SCSI driver. (Autoconfig stuff and PDMA functions.) + * The machine-independent parts are in ncr5380sbc.c + * + * Supported hardware includes: + * Macintosh II family 5380-based controller + * + * Credits, history: + * + * Scott Reynolds wrote this module, based on work by Allen Briggs + * (mac68k), David Jones (sun3), and Leo Weppelman (atari). Allen + * supplied some crucial interpretation of the NetBSD 1.1 'ncrscsi' + * driver. Allen, Gordon W. Ross, and Jason Thorpe all helped to + * refine this code, and were considerable sources of moral support. + * + * The sbc_options code is based on similar code in Jason's modified + * NetBSD/sparc 'si' driver. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/errno.h> +#include <sys/device.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/user.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsi_debug.h> +#include <scsi/scsiconf.h> + +#include <dev/ic/ncr5380reg.h> +#include <dev/ic/ncr5380var.h> + +#include <machine/viareg.h> + +#include "sbcreg.h" + +/* + * Transfers smaller than this are done using PIO + * (on assumption they're not worth PDMA overhead) + */ +#define MIN_DMA_LEN 128 + +/* + * Transfers larger than 8192 bytes need to be split up + * due to the size of the PDMA space. + */ +#define MAX_DMA_LEN 0x2000 + +/* + * From Guide to the Macintosh Family Hardware, p. 137 + * These are offsets from SCSIBase (see pmap_bootstrap.c) + */ +#define SBC_REGISTER_OFFSET 0x10000 +#define SBC_DMA_DRQ_OFFSET 0x06000 +#define SBC_DMA_NODRQ_OFFSET 0x12000 + +#ifdef SBC_DEBUG +# define SBC_DB_INTR 0x01 +# define SBC_DB_DMA 0x02 +# define SBC_DB_REG 0x04 +# define SBC_DB_BREAK 0x08 + + int sbc_debug = 0 /* | SBC_DB_INTR | SBC_DB_DMA */; + int sbc_link_flags = 0 /* | SDEV_DB2 */; + +# ifndef DDB +# define Debugger() printf("Debug: sbc.c:%d\n", __LINE__) +# endif +# define SBC_BREAK \ + do { if (sbc_debug & SBC_DB_BREAK) Debugger(); } while (0) +#else +# define SBC_BREAK +#endif + +/* + * This structure is used to keep track of PDMA requests. + */ +struct sbc_pdma_handle { + int dh_flags; /* flags */ +#define SBC_DH_BUSY 0x01 /* This handle is in use */ +#define SBC_DH_OUT 0x02 /* PDMA data out (write) */ + u_char *dh_addr; /* data buffer */ + int dh_len; /* length of data buffer */ +}; + +/* + * The first structure member has to be the ncr5380_softc + * so we can just cast to go back and forth between them. + */ +struct sbc_softc { + struct ncr5380_softc ncr_sc; + volatile struct sbc_regs *sc_regs; + volatile long *sc_drq_addr; + volatile u_char *sc_nodrq_addr; + volatile u_char *sc_ienable; + volatile u_char *sc_iflag; + int sc_options; /* options for this instance. */ + struct sbc_pdma_handle sc_pdma[SCI_OPENINGS]; +}; + +/* + * Options. By default, SCSI interrupts and reselect are disabled. + * You may enable either of these features with the `flags' directive + * in your kernel's configuration file. + * + * Alternatively, you can patch your kernel with DDB or some other + * mechanism. The sc_options member of the softc is OR'd with + * the value in sbc_options. + */ +#define SBC_PDMA 0x01 /* Use PDMA for polled transfers */ +#define SBC_INTR 0x02 /* Allow SCSI IRQ/DRQ interrupts */ +#define SBC_RESELECT 0x04 /* Allow disconnect/reselect */ +#define SBC_OPTIONS_MASK (SBC_RESELECT|SBC_INTR|SBC_PDMA) +#define SBC_OPTIONS_BITS "\10\3RESELECT\2INTR\1PDMA" +int sbc_options = SBC_PDMA; + +static int sbc_match __P((struct device *, void *, void *)); +static void sbc_attach __P((struct device *, struct device *, void *)); +static int sbc_print __P((void *, char *)); +static void sbc_minphys __P((struct buf *bp)); + +static int sbc_wait_busy __P((struct ncr5380_softc *)); +static int sbc_ready __P((struct ncr5380_softc *)); +static int sbc_wait_dreq __P((struct ncr5380_softc *)); +static int sbc_pdma_in __P((struct ncr5380_softc *, int, int, u_char *)); +static int sbc_pdma_out __P((struct ncr5380_softc *, int, int, u_char *)); +#ifdef SBC_DEBUG +static void decode_5380_intr __P((struct ncr5380_softc *)); +#endif + + void sbc_intr_enable __P((struct ncr5380_softc *)); + void sbc_intr_disable __P((struct ncr5380_softc *)); + void sbc_irq_intr __P((void *)); + void sbc_drq_intr __P((void *)); + void sbc_dma_alloc __P((struct ncr5380_softc *)); + void sbc_dma_free __P((struct ncr5380_softc *)); + void sbc_dma_poll __P((struct ncr5380_softc *)); + void sbc_dma_setup __P((struct ncr5380_softc *)); + void sbc_dma_start __P((struct ncr5380_softc *)); + void sbc_dma_eop __P((struct ncr5380_softc *)); + void sbc_dma_stop __P((struct ncr5380_softc *)); + +static struct scsi_adapter sbc_ops = { + ncr5380_scsi_cmd, /* scsi_cmd() */ + sbc_minphys, /* scsi_minphys() */ + NULL, /* open_target_lu() */ + NULL, /* close_target_lu() */ +}; + +/* This is copied from julian's bt driver */ +/* "so we have a default dev struct for our link struct." */ +static struct scsi_device sbc_dev = { + NULL, /* Use default error handler. */ + NULL, /* Use default start handler. */ + NULL, /* Use default async handler. */ + NULL, /* Use default "done" routine. */ +}; + +struct cfattach sbc_ca = { + sizeof(struct sbc_softc), sbc_match, sbc_attach +}; + +struct cfdriver sbc_cd = { + NULL, "sbc", DV_DULL +}; + + +static int +sbc_match(parent, match, args) + struct device *parent; + void *match, *args; +{ + if (!mac68k_machine.scsi80) + return 0; + return 1; +} + +static void +sbc_attach(parent, self, args) + struct device *parent, *self; + void *args; +{ + struct sbc_softc *sc = (struct sbc_softc *) self; + struct ncr5380_softc *ncr_sc = (struct ncr5380_softc *) sc; + extern vm_offset_t SCSIBase; + + /* Pull in the options flags. */ + sc->sc_options = ((ncr_sc->sc_dev.dv_cfdata->cf_flags | sbc_options) + & SBC_OPTIONS_MASK); + + /* + * Set up base address of 5380 + */ + sc->sc_regs = (struct sbc_regs *)(SCSIBase + SBC_REGISTER_OFFSET); + + /* + * Fill in the prototype scsi_link. + */ + ncr_sc->sc_link.adapter_softc = sc; + ncr_sc->sc_link.adapter_target = 7; + ncr_sc->sc_link.adapter = &sbc_ops; + ncr_sc->sc_link.device = &sbc_dev; + + /* + * Initialize fields used by the MI code + */ + ncr_sc->sci_r0 = &sc->sc_regs->sci_pr0.sci_reg; + ncr_sc->sci_r1 = &sc->sc_regs->sci_pr1.sci_reg; + ncr_sc->sci_r2 = &sc->sc_regs->sci_pr2.sci_reg; + ncr_sc->sci_r3 = &sc->sc_regs->sci_pr3.sci_reg; + ncr_sc->sci_r4 = &sc->sc_regs->sci_pr4.sci_reg; + ncr_sc->sci_r5 = &sc->sc_regs->sci_pr5.sci_reg; + ncr_sc->sci_r6 = &sc->sc_regs->sci_pr6.sci_reg; + ncr_sc->sci_r7 = &sc->sc_regs->sci_pr7.sci_reg; + + /* + * MD function pointers used by the MI code. + */ + ncr_sc->sc_pio_out = sbc_pdma_out; + ncr_sc->sc_pio_in = sbc_pdma_in; + ncr_sc->sc_dma_alloc = NULL; + ncr_sc->sc_dma_free = NULL; + ncr_sc->sc_dma_poll = NULL; + ncr_sc->sc_intr_on = NULL; + ncr_sc->sc_intr_off = NULL; + ncr_sc->sc_dma_setup = NULL; + ncr_sc->sc_dma_start = NULL; + ncr_sc->sc_dma_eop = NULL; + ncr_sc->sc_dma_stop = NULL; + ncr_sc->sc_flags = 0; + ncr_sc->sc_min_dma_len = MIN_DMA_LEN; + + if ((sc->sc_options & SBC_INTR) == 0) { + ncr_sc->sc_flags |= NCR5380_FORCE_POLLING; + } else { + if (sc->sc_options & SBC_RESELECT) + ncr_sc->sc_flags |= NCR5380_PERMIT_RESELECT; + ncr_sc->sc_dma_alloc = sbc_dma_alloc; + ncr_sc->sc_dma_free = sbc_dma_free; + ncr_sc->sc_dma_poll = sbc_dma_poll; + ncr_sc->sc_dma_setup = sbc_dma_setup; + ncr_sc->sc_dma_start = sbc_dma_start; + ncr_sc->sc_dma_eop = sbc_dma_eop; + ncr_sc->sc_dma_stop = sbc_dma_stop; + mac68k_register_scsi_drq(sbc_drq_intr, ncr_sc); + mac68k_register_scsi_irq(sbc_irq_intr, ncr_sc); + } + + /* + * Initialize fields used only here in the MD code. + */ + sc->sc_drq_addr = (long *) (SCSIBase + SBC_DMA_DRQ_OFFSET); + sc->sc_nodrq_addr = (u_char *) (SCSIBase + SBC_DMA_NODRQ_OFFSET); + if (VIA2 == VIA2OFF) { + sc->sc_ienable = Via1Base + VIA2 * 0x2000 + vIER; + sc->sc_iflag = Via1Base + VIA2 * 0x2000 + vIFR; + } else { + sc->sc_ienable = Via1Base + VIA2 * 0x2000 + rIER; + sc->sc_iflag = Via1Base + VIA2 * 0x2000 + rIFR; + } + + if (sc->sc_options) + printf(": options=%b", sc->sc_options, SBC_OPTIONS_BITS); + printf("\n"); + + /* Now enable SCSI interrupts through VIA2, if appropriate */ + if (sc->sc_options & SBC_INTR) + sbc_intr_enable(ncr_sc); + +#ifdef SBC_DEBUG + if (sbc_debug) + printf("%s: softc=%p regs=%p\n", ncr_sc->sc_dev.dv_xname, + sc, sc->sc_regs); + ncr_sc->sc_link.flags |= sbc_link_flags; +#endif + + /* + * Initialize the SCSI controller itself. + */ + ncr5380_init(ncr_sc); + ncr5380_reset_scsibus(ncr_sc); + config_found(self, &(ncr_sc->sc_link), sbc_print); +} + +static int +sbc_print(aux, name) + void *aux; + char *name; +{ + if (name != NULL) + printf("%s: scsibus ", name); + return UNCONF; +} + +static void +sbc_minphys(struct buf *bp) +{ + if (bp->b_bcount > MAX_DMA_LEN) + bp->b_bcount = MAX_DMA_LEN; + return (minphys(bp)); +} + + +/*** + * General support for Mac-specific SCSI logic. + ***/ + +/* These are used in the following inline functions. */ +int sbc_wait_busy_timo = 1000 * 5000; /* X2 = 10 S. */ +int sbc_ready_timo = 1000 * 5000; /* X2 = 10 S. */ +int sbc_wait_dreq_timo = 1000 * 5000; /* X2 = 10 S. */ + +/* Return zero on success. */ +static __inline__ int +sbc_wait_busy(sc) + struct ncr5380_softc *sc; +{ + register int timo = sbc_wait_busy_timo; + for (;;) { + if (SCI_BUSY(sc)) { + timo = 0; /* return 0 */ + break; + } + if (--timo < 0) + break; /* return -1 */ + delay(2); + } + return (timo); +} + +static __inline__ int +sbc_ready(sc) + struct ncr5380_softc *sc; +{ + register int timo = sbc_ready_timo; + + for (;;) { + if ((*sc->sci_csr & (SCI_CSR_DREQ|SCI_CSR_PHASE_MATCH)) + == (SCI_CSR_DREQ|SCI_CSR_PHASE_MATCH)) { + timo = 0; + break; + } + if (((*sc->sci_csr & SCI_CSR_PHASE_MATCH) == 0) + || (SCI_BUSY(sc) == 0)) { + timo = -1; + break; + } + if (--timo < 0) + break; /* return -1 */ + delay(2); + } + return (timo); +} + +static __inline__ int +sbc_wait_dreq(sc) + struct ncr5380_softc *sc; +{ + register int timo = sbc_wait_dreq_timo; + + for (;;) { + if ((*sc->sci_csr & (SCI_CSR_DREQ|SCI_CSR_PHASE_MATCH)) + == (SCI_CSR_DREQ|SCI_CSR_PHASE_MATCH)) { + timo = 0; + break; + } + if (--timo < 0) + break; /* return -1 */ + delay(2); + } + return (timo); +} + + +/*** + * Macintosh SCSI interrupt support routines. + ***/ + +void +sbc_intr_enable(ncr_sc) + struct ncr5380_softc *ncr_sc; +{ + register struct sbc_softc *sc = (struct sbc_softc *) ncr_sc; + int s; + + s = splhigh(); + *sc->sc_ienable = 0x80 | (V2IF_SCSIIRQ | V2IF_SCSIDRQ); + splx(s); +} + +void +sbc_intr_disable(ncr_sc) + struct ncr5380_softc *ncr_sc; +{ + register struct sbc_softc *sc = (struct sbc_softc *) ncr_sc; + int s; + + s = splhigh(); + *sc->sc_ienable = (V2IF_SCSIIRQ | V2IF_SCSIDRQ); + splx(s); +} + +void +sbc_irq_intr(p) + void *p; +{ + register struct ncr5380_softc *ncr_sc = p; + register int claimed = 0; + + /* How we ever arrive here without IRQ set is a mystery... */ + if (*ncr_sc->sci_csr & SCI_CSR_INT) { +#ifdef SBC_DEBUG + if (sbc_debug & SBC_DB_INTR) + decode_5380_intr(ncr_sc); +#endif + claimed = ncr5380_intr(ncr_sc); + if (!claimed) { + if (((*ncr_sc->sci_csr & ~SCI_CSR_PHASE_MATCH) == SCI_CSR_INT) + && ((*ncr_sc->sci_bus_csr & ~SCI_BUS_RST) == 0)) + SCI_CLR_INTR(ncr_sc); /* RST interrupt */ +#ifdef SBC_DEBUG + else { + printf("%s: spurious intr\n", + ncr_sc->sc_dev.dv_xname); + SBC_BREAK; + } +#endif + } + } +} + +#ifdef SBC_DEBUG +void +decode_5380_intr(ncr_sc) + struct ncr5380_softc *ncr_sc; +{ + register u_char csr = *ncr_sc->sci_csr; + register u_char bus_csr = *ncr_sc->sci_bus_csr; + + if (((csr & ~(SCI_CSR_PHASE_MATCH | SCI_CSR_ATN)) == SCI_CSR_INT) && + ((bus_csr & ~(SCI_BUS_MSG | SCI_BUS_CD | SCI_BUS_IO | SCI_BUS_DBP)) == SCI_BUS_SEL)) { + if (csr & SCI_BUS_IO) + printf("%s: reselect\n", ncr_sc->sc_dev.dv_xname); + else + printf("%s: select\n", ncr_sc->sc_dev.dv_xname); + } else if (((csr & ~SCI_CSR_ACK) == (SCI_CSR_DONE | SCI_CSR_INT)) && + ((bus_csr & (SCI_BUS_RST | SCI_BUS_BSY | SCI_BUS_SEL)) == SCI_BUS_BSY)) + printf("%s: dma eop\n", ncr_sc->sc_dev.dv_xname); + else if (((csr & ~SCI_CSR_PHASE_MATCH) == SCI_CSR_INT) && + ((bus_csr & ~SCI_BUS_RST) == 0)) + printf("%s: bus reset\n", ncr_sc->sc_dev.dv_xname); + else if (((csr & ~(SCI_CSR_DREQ | SCI_CSR_ATN | SCI_CSR_ACK)) == (SCI_CSR_PERR | SCI_CSR_INT | SCI_CSR_PHASE_MATCH)) && + ((bus_csr & (SCI_BUS_RST | SCI_BUS_BSY | SCI_BUS_SEL)) == SCI_BUS_BSY)) + printf("%s: parity error\n", ncr_sc->sc_dev.dv_xname); + else if (((csr & ~SCI_CSR_ATN) == SCI_CSR_INT) && + ((bus_csr & (SCI_BUS_RST | SCI_BUS_BSY | SCI_BUS_REQ | SCI_BUS_SEL)) == (SCI_BUS_BSY | SCI_BUS_REQ))) + printf("%s: phase mismatch\n", ncr_sc->sc_dev.dv_xname); + else if (((csr & ~SCI_CSR_PHASE_MATCH) == (SCI_CSR_INT | SCI_CSR_DISC)) && + (bus_csr == 0)) + printf("%s: disconnect\n", ncr_sc->sc_dev.dv_xname); + else + printf("%s: unknown intr: csr=%x, bus_csr=%x\n", + ncr_sc->sc_dev.dv_xname, csr, bus_csr); +} +#endif + +/*** + * The following code implements polled PDMA. + ***/ + +static int +sbc_pdma_out(ncr_sc, phase, count, data) + struct ncr5380_softc *ncr_sc; + int phase; + int count; + u_char *data; +{ + struct sbc_softc *sc = (struct sbc_softc *)ncr_sc; + register volatile long *long_data = sc->sc_drq_addr; + register volatile u_char *byte_data = sc->sc_nodrq_addr; + register int len = count; + + if (count < ncr_sc->sc_min_dma_len || (sc->sc_options & SBC_PDMA) == 0) + return ncr5380_pio_out(ncr_sc, phase, count, data); + + if (sbc_wait_busy(ncr_sc) == 0) { + *ncr_sc->sci_mode |= SCI_MODE_DMA; + *ncr_sc->sci_icmd |= SCI_ICMD_DATA; + *ncr_sc->sci_dma_send = 0; + +#define W1 *byte_data = *data++ +#define W4 *long_data = *((long*)data)++ + while (len >= 64) { + if (sbc_ready(ncr_sc)) + goto timeout; + W1; + if (sbc_ready(ncr_sc)) + goto timeout; + W1; + if (sbc_ready(ncr_sc)) + goto timeout; + W1; + if (sbc_ready(ncr_sc)) + goto timeout; + W1; + if (sbc_ready(ncr_sc)) + goto timeout; + W4; W4; W4; W4; + W4; W4; W4; W4; + W4; W4; W4; W4; + W4; W4; W4; + len -= 64; + } + while (len) { + if (sbc_ready(ncr_sc)) + goto timeout; + W1; + len--; + } +#undef W1 +#undef W4 + if (sbc_wait_dreq(ncr_sc)) + printf("%s: timeout waiting for DREQ.\n", + ncr_sc->sc_dev.dv_xname); + + *byte_data = 0; + + SCI_CLR_INTR(ncr_sc); + *ncr_sc->sci_mode &= ~SCI_MODE_DMA; + *ncr_sc->sci_icmd = 0; + } + return count - len; + +timeout: + printf("%s: pdma_out: timeout len=%d count=%d\n", + ncr_sc->sc_dev.dv_xname, len, count); + if ((*ncr_sc->sci_csr & SCI_CSR_PHASE_MATCH) == 0) { + *ncr_sc->sci_icmd &= ~SCI_ICMD_DATA; + --len; + } + + SCI_CLR_INTR(ncr_sc); + *ncr_sc->sci_mode &= ~SCI_MODE_DMA; + *ncr_sc->sci_icmd = 0; + return count - len; +} + +static int +sbc_pdma_in(ncr_sc, phase, count, data) + struct ncr5380_softc *ncr_sc; + int phase; + int count; + u_char *data; +{ + struct sbc_softc *sc = (struct sbc_softc *)ncr_sc; + register volatile long *long_data = sc->sc_drq_addr; + register volatile u_char *byte_data = sc->sc_nodrq_addr; + register int len = count; + + if (count < ncr_sc->sc_min_dma_len || (sc->sc_options & SBC_PDMA) == 0) + return ncr5380_pio_in(ncr_sc, phase, count, data); + + if (sbc_wait_busy(ncr_sc) == 0) { + *ncr_sc->sci_mode |= SCI_MODE_DMA; + *ncr_sc->sci_icmd |= SCI_ICMD_DATA; + *ncr_sc->sci_irecv = 0; + +#define R4 *((long *)data)++ = *long_data +#define R1 *data++ = *byte_data + while (len >= 1024) { + if (sbc_ready(ncr_sc)) + goto timeout; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; /* 128 */ + if (sbc_ready(ncr_sc)) + goto timeout; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; /* 256 */ + if (sbc_ready(ncr_sc)) + goto timeout; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; /* 384 */ + if (sbc_ready(ncr_sc)) + goto timeout; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; /* 512 */ + if (sbc_ready(ncr_sc)) + goto timeout; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; /* 640 */ + if (sbc_ready(ncr_sc)) + goto timeout; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; /* 768 */ + if (sbc_ready(ncr_sc)) + goto timeout; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; /* 896 */ + if (sbc_ready(ncr_sc)) + goto timeout; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; /* 1024 */ + len -= 1024; + } + while (len >= 128) { + if (sbc_ready(ncr_sc)) + goto timeout; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; /* 128 */ + len -= 128; + } + while (len) { + if (sbc_ready(ncr_sc)) + goto timeout; + R1; + len--; + } +#undef R4 +#undef R1 + SCI_CLR_INTR(ncr_sc); + *ncr_sc->sci_mode &= ~SCI_MODE_DMA; + *ncr_sc->sci_icmd = 0; + } + return count - len; + +timeout: + printf("%s: pdma_in: timeout len=%d count=%d\n", + ncr_sc->sc_dev.dv_xname, len, count); + + SCI_CLR_INTR(ncr_sc); + *ncr_sc->sci_mode &= ~SCI_MODE_DMA; + *ncr_sc->sci_icmd = 0; + return count - len; +} + + +/*** + * The following code implements interrupt-driven PDMA. + ***/ + +/* + * This is the meat of the PDMA transfer. + * When we get here, we shove data as fast as the mac can take it. + * We depend on several things: + * * All macs after the Mac Plus that have a 5380 chip should have a general + * logic IC that handshakes data for blind transfers. + * * If the SCSI controller finishes sending/receiving data before we do, + * the same general logic IC will generate a /BERR for us in short order. + * * The fault address for said /BERR minus the base address for the + * transfer will be the amount of data that was actually written. + * + * We use the nofault flag and the setjmp/longjmp in locore.s so we can + * detect and handle the bus error for early termination of a command. + * This is usually caused by a disconnecting target. + */ +void +sbc_drq_intr(p) + void *p; +{ + extern int *nofault, mac68k_buserr_addr; + register struct sbc_softc *sc = (struct sbc_softc *) p; + register struct ncr5380_softc *ncr_sc = (struct ncr5380_softc *) p; + register struct sci_req *sr = ncr_sc->sc_current; + register struct sbc_pdma_handle *dh = sr->sr_dma_hand; + label_t faultbuf; + volatile u_int32_t *long_drq; + u_int32_t *long_data; + volatile u_int8_t *drq; + u_int8_t *data; + register int count; + int dcount, resid; + + /* + * If we're not ready to xfer data, or have no more, just return. + */ + if ((*ncr_sc->sci_csr & SCI_CSR_DREQ) == 0 || dh->dh_len == 0) + return; + +#ifdef SBC_DEBUG + if (sbc_debug & SBC_DB_INTR) + printf("%s: drq intr, dh_len=0x%x, dh_flags=0x%x\n", + ncr_sc->sc_dev.dv_xname, dh->dh_len, dh->dh_flags); +#endif + + /* + * Setup for a possible bus error caused by SCSI controller + * switching out of DATA-IN/OUT before we're done with the + * current transfer. + */ + nofault = (int *) &faultbuf; + + if (setjmp((label_t *) nofault)) { + nofault = (int *) 0; + count = ( (u_long) mac68k_buserr_addr + - (u_long) sc->sc_drq_addr); + + if ((count < 0) || (count > dh->dh_len)) { + printf("%s: complete=0x%x (pending 0x%x)\n", + ncr_sc->sc_dev.dv_xname, count, dh->dh_len); + panic("something is wrong"); + } +#ifdef SBC_DEBUG + if (sbc_debug & SBC_DB_INTR) + printf("%s: drq /berr, pending=0x%x, complete=0x%x\n", + ncr_sc->sc_dev.dv_xname, dh->dh_len, count); +#endif + + dh->dh_addr += count; + dh->dh_len -= count; + mac68k_buserr_addr = 0; + + return; + } + + if (dh->dh_flags & SBC_DH_OUT) { /* Data Out */ + /* + * Get the source address aligned. + */ + resid = + count = min(dh->dh_len, 4 - (((int) dh->dh_addr) & 0x3)); + if (count && count < 4) { + data = (u_int8_t *) dh->dh_addr; + drq = (u_int8_t *) sc->sc_drq_addr; +#define W1 *drq++ = *data++ + while (count) { + W1; count--; + } +#undef W1 + dh->dh_addr += resid; + dh->dh_len -= resid; + } + + /* + * Get ready to start the transfer. + */ + while (dh->dh_len) { + dcount = count = min(dh->dh_len, MAX_DMA_LEN); + long_drq = (volatile u_int32_t *) sc->sc_drq_addr; + long_data = (u_int32_t *) dh->dh_addr; + +#define W4 *long_drq++ = *long_data++ + while (count >= 64) { + W4; W4; W4; W4; W4; W4; W4; W4; + W4; W4; W4; W4; W4; W4; W4; W4; /* 64 */ + count -= 64; + } + while (count >= 4) { + W4; count -= 4; + } +#undef W4 + data = (u_int8_t *) long_data; + drq = (u_int8_t *) long_drq; +#define W1 *drq++ = *data++ + while (count) { + W1; count--; + } +#undef W1 + dh->dh_len -= dcount; + dh->dh_addr += dcount; + } + } else { /* Data In */ + /* + * Get the dest address aligned. + */ + resid = + count = min(dh->dh_len, 4 - (((int) dh->dh_addr) & 0x3)); + if (count && count < 4) { + data = (u_int8_t *) dh->dh_addr; + drq = (u_int8_t *) sc->sc_drq_addr; +#define R1 *data++ = *drq++ + while (count) { + R1; count--; + } +#undef R1 + dh->dh_addr += resid; + dh->dh_len -= resid; + } + + /* + * Get ready to start the transfer. + */ + while (dh->dh_len) { + dcount = count = min(dh->dh_len, MAX_DMA_LEN); + long_drq = (volatile u_int32_t *) sc->sc_drq_addr; + long_data = (u_int32_t *) dh->dh_addr; + +#define R4 *long_data++ = *long_drq++ + while (count >= 512) { + if ((*ncr_sc->sci_csr & SCI_CSR_DREQ) == 0) { + nofault = (int *) 0; + + dh->dh_addr += (dcount - count); + dh->dh_len -= (dcount - count); + return; + } + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; /* 64 */ + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; /* 128 */ + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; /* 256 */ + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; + R4; R4; R4; R4; R4; R4; R4; R4; /* 512 */ + count -= 512; + } + while (count >= 4) { + R4; count -= 4; + } +#undef R4 + data = (u_int8_t *) long_data; + drq = (u_int8_t *) long_drq; +#define R1 *data++ = *drq++ + while (count) { + R1; count--; + } +#undef R1 + dh->dh_len -= dcount; + dh->dh_addr += dcount; + } + } + + /* + * OK. No bus error occurred above. Clear the nofault flag + * so we no longer short-circuit bus errors. + */ + nofault = (int *) 0; +} + +void +sbc_dma_alloc(ncr_sc) + struct ncr5380_softc *ncr_sc; +{ + struct sbc_softc *sc = (struct sbc_softc *) ncr_sc; + struct sci_req *sr = ncr_sc->sc_current; + struct scsi_xfer *xs = sr->sr_xs; + struct sbc_pdma_handle *dh; + int i, xlen; + +#ifdef DIAGNOSTIC + if (sr->sr_dma_hand != NULL) + panic("sbc_dma_alloc: already have PDMA handle"); +#endif + + /* Polled transfers shouldn't allocate a PDMA handle. */ + if (sr->sr_flags & SR_IMMED) + return; + +#ifndef SBCTEST + /* XXX - we don't trust PDMA writes yet! */ + if (xs->flags & SCSI_DATA_OUT) + return; +#endif + + xlen = ncr_sc->sc_datalen; + + /* Make sure our caller checked sc_min_dma_len. */ + if (xlen < MIN_DMA_LEN) + panic("sbc_dma_alloc: len=0x%x\n", xlen); + + /* + * Find free PDMA handle. Guaranteed to find one since we + * have as many PDMA handles as the driver has processes. + * (instances?) + */ + for (i = 0; i < SCI_OPENINGS; i++) { + if ((sc->sc_pdma[i].dh_flags & SBC_DH_BUSY) == 0) + goto found; + } + panic("sbc: no free PDMA handles"); +found: + dh = &sc->sc_pdma[i]; + dh->dh_flags = SBC_DH_BUSY; + dh->dh_addr = ncr_sc->sc_dataptr; + dh->dh_len = xlen; + + /* Copy the 'write' flag for convenience. */ + if (xs->flags & SCSI_DATA_OUT) + dh->dh_flags |= SBC_DH_OUT; + + sr->sr_dma_hand = dh; +} + +void +sbc_dma_free(ncr_sc) + struct ncr5380_softc *ncr_sc; +{ + struct sci_req *sr = ncr_sc->sc_current; + struct sbc_pdma_handle *dh = sr->sr_dma_hand; + +#ifdef DIAGNOSTIC + if (sr->sr_dma_hand == NULL) + panic("sbc_dma_free: no DMA handle"); +#endif + + if (ncr_sc->sc_state & NCR_DOINGDMA) + panic("sbc_dma_free: free while in progress"); + + if (dh->dh_flags & SBC_DH_BUSY) { + dh->dh_flags = 0; + dh->dh_addr = NULL; + dh->dh_len = 0; + } + sr->sr_dma_hand = NULL; +} + +void +sbc_dma_poll(ncr_sc) + struct ncr5380_softc *ncr_sc; +{ + struct sci_req *sr = ncr_sc->sc_current; + + /* + * We shouldn't arrive here; if SR_IMMED is set, then + * dma_alloc() should have refused to allocate a handle + * for the transfer. This forces the polled PDMA code + * to handle the request... + */ +#ifdef SBC_DEBUG + if (sbc_debug & SBC_DB_DMA) + printf("%s: lost DRQ interrupt?\n", ncr_sc->sc_dev.dv_xname); +#endif + sr->sr_flags |= SR_OVERDUE; +} + +void +sbc_dma_setup(ncr_sc) + struct ncr5380_softc *ncr_sc; +{ + /* Not needed; we don't have real DMA */ +} + +void +sbc_dma_start(ncr_sc) + struct ncr5380_softc *ncr_sc; +{ + struct sci_req *sr = ncr_sc->sc_current; + struct sbc_pdma_handle *dh = sr->sr_dma_hand; + + /* + * Match bus phase, set DMA mode, and assert data bus (for + * writing only), then start the transfer. + */ + if (dh->dh_flags & SBC_DH_OUT) { + *ncr_sc->sci_tcmd = PHASE_DATA_OUT; + SCI_CLR_INTR(ncr_sc); + *ncr_sc->sci_mode |= SCI_MODE_DMA; + *ncr_sc->sci_icmd = SCI_ICMD_DATA; + *ncr_sc->sci_dma_send = 0; + } else { + *ncr_sc->sci_tcmd = PHASE_DATA_IN; + SCI_CLR_INTR(ncr_sc); + *ncr_sc->sci_mode |= SCI_MODE_DMA; + *ncr_sc->sci_icmd = 0; + *ncr_sc->sci_irecv = 0; + } + ncr_sc->sc_state |= NCR_DOINGDMA; + +#ifdef SBC_DEBUG + if (sbc_debug & SBC_DB_DMA) + printf("%s: PDMA started, va=%p, len=0x%x\n", + ncr_sc->sc_dev.dv_xname, dh->dh_addr, dh->dh_len); +#endif +} + +void +sbc_dma_eop(ncr_sc) + struct ncr5380_softc *ncr_sc; +{ + /* Not used; the EOP pin is wired high (GMFH, pp. 389-390) */ +} + +void +sbc_dma_stop(ncr_sc) + struct ncr5380_softc *ncr_sc; +{ + struct sci_req *sr = ncr_sc->sc_current; + struct sbc_pdma_handle *dh = sr->sr_dma_hand; + register int ntrans; + + if ((ncr_sc->sc_state & NCR_DOINGDMA) == 0) { +#ifdef SBC_DEBUG + if (sbc_debug & SBC_DB_DMA) + printf("%s: dma_stop: DMA not running\n", + ncr_sc->sc_dev.dv_xname); +#endif + return; + } + ncr_sc->sc_state &= ~NCR_DOINGDMA; + + if ((ncr_sc->sc_state & NCR_ABORTING) == 0) { + ntrans = ncr_sc->sc_datalen - dh->dh_len; + +#ifdef SBC_DEBUG + if (sbc_debug & SBC_DB_DMA) + printf("%s: dma_stop: ntrans=0x%x\n", + ncr_sc->sc_dev.dv_xname, ntrans); +#endif + + if (ntrans > ncr_sc->sc_datalen) + panic("sbc_dma_stop: excess transfer\n"); + + /* Adjust data pointer */ + ncr_sc->sc_dataptr += ntrans; + ncr_sc->sc_datalen -= ntrans; + + /* Clear any pending interrupts. */ + SCI_CLR_INTR(ncr_sc); + } + + /* Put SBIC back into PIO mode. */ + *ncr_sc->sci_mode &= ~SCI_MODE_DMA; + *ncr_sc->sci_icmd = 0; + +#ifdef SBC_DEBUG + if (sbc_debug & SBC_DB_REG) + printf("%s: dma_stop: csr=0x%x, bus_csr=0x%x\n", + ncr_sc->sc_dev.dv_xname, *ncr_sc->sci_csr, + *ncr_sc->sci_bus_csr); +#endif +} diff --git a/sys/arch/mac68k/dev/sbcreg.h b/sys/arch/mac68k/dev/sbcreg.h new file mode 100644 index 00000000000..6daf826b440 --- /dev/null +++ b/sys/arch/mac68k/dev/sbcreg.h @@ -0,0 +1,28 @@ +/* $OpenBSD: sbcreg.h,v 1.1 1996/05/26 19:02:10 briggs Exp $ */ +/* $NetBSD: sbcreg.h,v 1.1 1996/04/25 22:26:56 scottr Exp $ */ + +/* + * Register map for the Mac II SCSI Interface (sbc) + * This register map is for the SYM/NCR5380 SCSI Bus Interface + * Controller (SBIC), with the wonderful 16 bytes/register layout + * that Macs have. + */ + +/* + * Am5380 Register map (with padding) + */ +typedef union { + volatile u_char sci_reg; + volatile u_char pad[16]; +} ncr5380_padded_reg_t; + +struct sbc_regs { + ncr5380_padded_reg_t sci_pr0; + ncr5380_padded_reg_t sci_pr1; + ncr5380_padded_reg_t sci_pr2; + ncr5380_padded_reg_t sci_pr3; + ncr5380_padded_reg_t sci_pr4; + ncr5380_padded_reg_t sci_pr5; + ncr5380_padded_reg_t sci_pr6; + ncr5380_padded_reg_t sci_pr7; +}; diff --git a/sys/arch/mac68k/dev/z8530sc.c b/sys/arch/mac68k/dev/z8530sc.c new file mode 100644 index 00000000000..2447c5233e0 --- /dev/null +++ b/sys/arch/mac68k/dev/z8530sc.c @@ -0,0 +1,396 @@ +/* $OpenBSD: z8530sc.c,v 1.1 1996/05/26 19:02:10 briggs Exp $ */ +/* $NetBSD: z8530sc.c,v 1.1 1996/05/18 18:54:28 briggs Exp $ */ + +/* + * Copyright (c) 1994 Gordon W. Ross + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)zs.c 8.1 (Berkeley) 7/19/93 + */ + +/* + * Zilog Z8530 Dual UART driver (common part) + * + * This file contains the machine-independent parts of the + * driver common to tty and keyboard/mouse sub-drivers. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/device.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/tty.h> +#include <sys/time.h> +#include <sys/kernel.h> +#include <sys/syslog.h> + +/* #include <dev/ic/z8530reg.h> */ +#include "z8530reg.h" +#include <machine/z8530var.h> + +int +zs_break(cs, set) + struct zs_chanstate *cs; + int set; +{ + int s; + + s = splzs(); + if (set) { + cs->cs_preg[5] |= ZSWR5_BREAK; + cs->cs_creg[5] |= ZSWR5_BREAK; + } else { + cs->cs_preg[5] &= ~ZSWR5_BREAK; + cs->cs_creg[5] &= ~ZSWR5_BREAK; + } + zs_write_reg(cs, 5, cs->cs_creg[5]); + splx(s); + + return 0; +} + + +/* + * Compute the current baud rate given a ZSCC channel. + */ +int +zs_getspeed(cs) + struct zs_chanstate *cs; +{ + int tconst; + + tconst = zs_read_reg(cs, 12); + tconst |= zs_read_reg(cs, 13) << 8; + return (TCONST_TO_BPS(cs->cs_pclk_div16, tconst)); +} + +/* + * drain on-chip fifo + */ +void +zs_iflush(cs) + struct zs_chanstate *cs; +{ + u_char c, rr0, rr1; + + for (;;) { + /* Is there input available? */ + rr0 = zs_read_csr(cs); + if ((rr0 & ZSRR0_RX_READY) == 0) + break; + + /* + * First read the status, because reading the data + * destroys the status of this char. + */ + rr1 = zs_read_reg(cs, 1); + c = zs_read_data(cs); + + if (rr1 & (ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) { + /* Clear the receive error. */ + zs_write_csr(cs, ZSWR0_RESET_ERRORS); + } + } +} + +/* + * Figure out if a chip is an NMOS 8530, a CMOS 8530, + * or an 85230. We use a form of the test in the Zilog SCC + * users manual. + */ +int +zs_checkchip(cs) + struct zs_chanstate *cs; +{ + char r1, r2, r3; + int chip; + + /* we assume we can write to the chip */ + + r1=cs->cs_creg[15]; /* see if bit 0 sticks */ + zs_write_reg(cs, 15, (r1 | ZSWR15_ENABLE_ENHANCED)); + if ((zs_read_reg(cs, 15) & ZSWR15_ENABLE_ENHANCED) != 0) { + /* we have either an 8580 or 85230. NB Zilog says we should only + * have an 85230 at this point, but the 8580 seems to pass this + * test too. To test, we try to write to WR7', and see if we + * loose sight of RR14. */ + r2=cs->cs_creg[14]; + r3=(r2 != 0x47) ? 0x47 : 0x40; + /* unique bit pattern to turn on reading of WR7' at RR14 */ + zs_write_reg(cs, 7, ~r2); + if (zs_read_reg(cs, ZSRR_ENHANCED) != r2) { + chip = ZS_CHIP_ESCC; + zs_write_reg(cs, 7, cs->cs_creg[ZS_ENHANCED_REG]); + } else { + chip = ZS_CHIP_8580; + zs_write_reg(cs, 7, cs->cs_creg[7]); + } + zs_write_reg(cs, 15, r1); + } else { /* now we have to tell an NMOS from a CMOS; does WR15 D2 work? */ + zs_write_reg(cs, 15, (r1 | ZSWR15_SDLC_FIFO)); + r2=cs->cs_creg[2]; + zs_write_reg(cs, 2, (r2 | 0x80)); + chip = (zs_read_reg(cs, 6) & 0x80) ? ZS_CHIP_NMOS : ZS_CHIP_CMOS; + zs_write_reg(cs, 2, r2); + } + zs_write_reg(cs, 15, r1); + return chip; +} + +/* + * Write the given register set to the given zs channel in the proper order. + * The channel must not be transmitting at the time. The receiver will + * be disabled for the time it takes to write all the registers. + * Call this with interrupts disabled. + */ +void +zs_loadchannelregs(cs) + struct zs_chanstate *cs; +{ + u_char *reg; + + /* Copy "pending" regs to "current" */ + bcopy((caddr_t)cs->cs_preg, (caddr_t)cs->cs_creg, 16); + reg = cs->cs_creg; /* current regs */ + + zs_write_csr(cs, ZSM_RESET_ERR); /* XXX: reset error condition */ + +#if 1 + /* + * XXX: Is this really a good idea? + * XXX: Should go elsewhere! -gwr + */ + zs_iflush(cs); /* XXX */ +#endif + + /* disable interrupts */ + zs_write_reg(cs, 1, reg[1] & + ~(ZSWR1_RIE_SPECIAL_ONLY | ZSWR1_TIE | ZSWR1_SIE)); + + /* baud clock divisor, stop bits, parity */ + zs_write_reg(cs, 4, reg[4]); + + /* misc. TX/RX control bits */ + zs_write_reg(cs, 10, reg[10]); + + /* char size, enable (RX/TX) */ + zs_write_reg(cs, 3, reg[3] & ~ZSWR3_RX_ENABLE); + zs_write_reg(cs, 5, reg[5] & ~ZSWR5_TX_ENABLE); + + /* synchronous mode stuff */ + zs_write_reg(cs, 6, reg[6]); + zs_write_reg(cs, 7, reg[7]); + +#if 0 + /* + * Registers 2 and 9 are special because they are + * actually common to both channels, but must be + * programmed through channel A. The "zsc" attach + * function takes care of setting these registers + * and they should not be touched thereafter. + */ + /* interrupt vector */ + zs_write_reg(cs, 2, reg[2]); + /* master interrupt control */ + zs_write_reg(cs, 9, reg[9]); +#endif + + /* Shut down the BRG */ + zs_write_reg(cs, 14, reg[14] & ~ZSWR14_BAUD_ENA); + + if ((cs->cs_cclk_flag & ZSC_EXTERN) || + (cs->cs_pclk_flag & ZSC_EXTERN)) + zsmd_setclock(cs); + /* the md layer wants to do something; let it. */ + + /* clock mode control */ + zs_write_reg(cs, 11, reg[11]); + + /* baud rate (lo/hi) */ + zs_write_reg(cs, 12, reg[12]); + zs_write_reg(cs, 13, reg[13]); + + /* Misc. control bits */ + zs_write_reg(cs, 14, reg[14]); + + /* which lines cause status interrupts */ + zs_write_reg(cs, 15, reg[15]); + + /* Zilog docs recommend resetting external status twice at this + * point. Mainly as the status bits are latched, and the first + * interrupt clear might unlatch them to new values, generating + * a second interrupt request. + */ + zs_write_csr(cs, ZSM_RESET_STINT); + zs_write_csr(cs, ZSM_RESET_STINT); + + /* char size, enable (RX/TX)*/ + zs_write_reg(cs, 3, reg[3]); + zs_write_reg(cs, 5, reg[5]); + + /* interrupt enables: TX, TX, STATUS */ + zs_write_reg(cs, 1, reg[1]); + + cs->cs_cclk_flag = cs->cs_pclk_flag; + cs->cs_csource = cs->cs_psource; +} + + +/* + * ZS hardware interrupt. Scan all ZS channels. NB: we know here that + * channels are kept in (A,B) pairs. + * + * Do just a little, then get out; set a software interrupt if more + * work is needed. + * + * We deliberately ignore the vectoring Zilog gives us, and match up + * only the number of `reset interrupt under service' operations, not + * the order. + */ +int +zsc_intr_hard(arg) + void *arg; +{ + register struct zsc_softc *zsc = arg; + register struct zs_chanstate *cs_a; + register struct zs_chanstate *cs_b; + register int rval; + register u_char rr3; + + cs_a = &zsc->zsc_cs[0]; + cs_b = &zsc->zsc_cs[1]; + rval = 0; + + /* Note: only channel A has an RR3 */ + rr3 = zs_read_reg(cs_a, 3); + + /* Handle receive interrupts first. */ + if (rr3 & ZSRR3_IP_A_RX) + (*cs_a->cs_ops->zsop_rxint)(cs_a); + if (rr3 & ZSRR3_IP_B_RX) + (*cs_b->cs_ops->zsop_rxint)(cs_b); + + /* Handle status interrupts (i.e. flow control). */ + if (rr3 & ZSRR3_IP_A_STAT) + (*cs_a->cs_ops->zsop_stint)(cs_a); + if (rr3 & ZSRR3_IP_B_STAT) + (*cs_b->cs_ops->zsop_stint)(cs_b); + + /* Handle transmit done interrupts. */ + if (rr3 & ZSRR3_IP_A_TX) + (*cs_a->cs_ops->zsop_txint)(cs_a); + if (rr3 & ZSRR3_IP_B_TX) + (*cs_b->cs_ops->zsop_txint)(cs_b); + + /* Clear interrupt. */ + if (rr3 & (ZSRR3_IP_A_RX | ZSRR3_IP_A_TX | ZSRR3_IP_A_STAT)) { + zs_write_csr(cs_a, ZSWR0_CLR_INTR); + rval |= 1; + } + if (rr3 & (ZSRR3_IP_B_RX | ZSRR3_IP_B_TX | ZSRR3_IP_B_STAT)) { + zs_write_csr(cs_b, ZSWR0_CLR_INTR); + rval |= 2; + } + + if ((cs_a->cs_softreq) || (cs_b->cs_softreq)) { + /* This is a machine-dependent function (or macro). */ + zsc_req_softint(zsc); + } + + return (rval); +} + + +/* + * ZS software interrupt. Scan all channels for deferred interrupts. + */ +int +zsc_intr_soft(arg) + void *arg; +{ + register struct zsc_softc *zsc = arg; + register struct zs_chanstate *cs; + register int rval, unit; + + rval = 0; + for (unit = 0; unit < 2; unit++) { + cs = &zsc->zsc_cs[unit]; + + /* + * The softint flag can be safely cleared once + * we have decided to call the softint routine. + * (No need to do splzs() first.) + */ + if (cs->cs_softreq) { + cs->cs_softreq = 0; + (*cs->cs_ops->zsop_softint)(cs); + rval = 1; + } + } + return (rval); +} + +static void zsnull_intr __P((struct zs_chanstate *)); +static void zsnull_softint __P((struct zs_chanstate *)); + +static void +zsnull_intr(cs) + struct zs_chanstate *cs; +{ + zs_write_reg(cs, 1, 0); + zs_write_reg(cs, 15, 0); +} + +static void +zsnull_softint(cs) + struct zs_chanstate *cs; +{ +} + +struct zsops zsops_null = { + zsnull_intr, /* receive char available */ + zsnull_intr, /* external/status */ + zsnull_intr, /* xmit buffer empty */ + zsnull_softint, /* process software interrupt */ +}; diff --git a/sys/arch/mac68k/dev/z8530sc.h b/sys/arch/mac68k/dev/z8530sc.h new file mode 100644 index 00000000000..ba19bff490d --- /dev/null +++ b/sys/arch/mac68k/dev/z8530sc.h @@ -0,0 +1,174 @@ +/* $OpenBSD: z8530sc.h,v 1.1 1996/05/26 19:02:11 briggs Exp $ */ +/* $NetBSD: z8530sc.h,v 1.1 1996/05/18 18:54:30 briggs Exp $ */ + +/* + * Copyright (c) 1994 Gordon W. Ross + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)zsvar.h 8.1 (Berkeley) 6/11/93 + */ + + +/* + * Clock source info structure + */ +struct zsclksrc { + long clk; /* clock rate, in MHz, present on signal line */ + int flags; /* Specifies how this source can be used + (RTxC divided, RTxC BRG, PCLK BRG, TRxC divided) + and also if the source is "external" and if it + is changeable (by an ioctl ex.). The + source usage flags are used by the tty + child. The other bits tell zsloadchannelregs + if it should call an md signal source + changing routine. ZSC_VARIABLE says if + an ioctl should be able to cahnge the + clock rate.*/ +}; +#define ZSC_PCLK 0x01 +#define ZSC_RTXBRG 0x02 +#define ZSC_RTXDIV 0x04 +#define ZSC_TRXDIV 0x08 +#define ZSC_VARIABLE 0x40 +#define ZSC_EXTERN 0x80 + +#define ZSC_BRG 0x03 +#define ZSC_DIV 0x0c + + +/* + * Software state, per zs channel. + */ +struct zs_chanstate { + + /* Pointers to the device registers. */ + volatile u_char *cs_reg_csr; /* ctrl, status, and reg. number. */ + volatile u_char *cs_reg_data; /* data or numbered register */ + + int cs_channel; /* sub-unit number */ + void *cs_private; /* sub-driver data pointer */ + struct zsops *cs_ops; + + int cs_defspeed; /* default baud rate (from PROM) */ + int cs_pclk_div16; /* PCLK / 16 used only by kbd & ms kids */ + int cs_clock_count; /* how many signal sources available */ + struct zsclksrc cs_clocks[4]; /* info on available signal sources */ + + /* + * We must keep a copy of the write registers as they are + * mostly write-only and we sometimes need to set and clear + * individual bits (e.g., in WR3). Not all of these are + * needed but 16 bytes is cheap and this makes the addressing + * simpler. Unfortunately, we can only write to some registers + * when the chip is not actually transmitting, so whenever + * we are expecting a `transmit done' interrupt the preg array + * is allowed to `get ahead' of the current values. In a + * few places we must change the current value of a register, + * rather than (or in addition to) the pending value; for these + * cs_creg[] contains the current value. + */ + u_char cs_creg[16]; /* current values */ + u_char cs_preg[16]; /* pending values */ + long cs_cclk_flag; /* flag for current clock source */ + long cs_pclk_flag; /* flag for pending clock source */ + int cs_csource; /* current source # */ + int cs_psource; /* pending source # */ + + u_char cs_heldchange; /* change pending (creg != preg) */ + u_char cs_rr0; /* last rr0 processed */ + u_char cs_rr0_new; /* rr0 saved in status interrupt. */ + + char cs_softreq; /* need soft interrupt call */ + char cs_chip; /* type of chip */ + char cs__spare; +}; +#define ZS_ENHANCED_REG 8 + /* cs_Xreg which is used to hold WR7' data; reg 8 is an alias to the + * data port, so we won't miss its loss. */ + +/* + * Function vector - per channel + */ +typedef void (*zsop_t)(register struct zs_chanstate *); +struct zsops { + zsop_t zsop_rxint; /* receive char available */ + zsop_t zsop_stint; /* external/status */ + zsop_t zsop_txint; /* xmit buffer empty */ + zsop_t zsop_softint; /* process software interrupt */ +}; + +extern struct zsops zsops_null; + +struct zsc_softc { + struct device zsc_dev; /* required first: base device */ + struct zs_chanstate zsc_cs[2]; /* channel A and B soft state */ +}; + +struct zsc_attach_args { + int channel; /* two serial channels per zsc */ + int hwflags; +}; +#define ZS_HWFLAG_CONSOLE 1 +#define ZS_HWFLAG_CONABRT 2 +#define ZS_HWFLAG_RAW 4 +#define ZS_HWFLAG_IGCTS 16 +#define ZS_HWFLAG_IGDCD 32 +/* _CONSOLE says this port is the console, _CONABRT says a Break sequence acts as + an abort, and _RAW recomends "raw" mode defaults on a tty. + _CONABRT is turned off if an overly-long break is received. + _IGCTS and _IGDCD tell the tty layer to ignore CTS or DCD. Assume + whatever's least supprising (CTS and DCD present). Used mainly for + external clock support on mac68k. The DCD and CTS pins are used also + for clock inputs; not good for the UNIX I/O model! */ + +#define ZS_CHIP_NMOS 0 +#define ZS_CHIP_CMOS 1 +#define ZS_CHIP_8580 2 +#define ZS_CHIP_ESCC 3 + +void zs_loadchannelregs __P((struct zs_chanstate *)); +int zsc_intr_soft __P((void *)); +int zsc_intr_hard __P((void *)); +int zs_checkchip __P((struct zs_chanstate *)); +int zs_break __P((struct zs_chanstate *, int)); +int zs_getspeed __P((struct zs_chanstate *)); +void zs_iflush __P((struct zs_chanstate *)); + diff --git a/sys/arch/mac68k/dev/z8530tty.c b/sys/arch/mac68k/dev/z8530tty.c new file mode 100644 index 00000000000..fa4cb3274de --- /dev/null +++ b/sys/arch/mac68k/dev/z8530tty.c @@ -0,0 +1,1433 @@ +/* $OpenBSD: z8530tty.c,v 1.1 1996/05/26 19:02:12 briggs Exp $ */ +/* $NetBSD: z8530tty.c,v 1.2 1996/05/23 02:06:53 briggs Exp $ */ + +/* + * Copyright (c) 1994 Gordon W. Ross + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)zs.c 8.1 (Berkeley) 7/19/93 + */ + +/* + * Zilog Z8530 Dual UART driver (tty interface) + * + * This is the "slave" driver that will be attached to + * the "zsc" driver for plain "tty" async. serial lines. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/device.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/malloc.h> +#include <sys/tty.h> +#include <sys/time.h> +#include <sys/kernel.h> +#include <sys/syslog.h> + +/* #include <dev/ic/z8530reg.h> */ +#include "z8530reg.h" +#include <machine/z8530var.h> + +#ifdef KGDB +extern int zs_check_kgdb(); +#endif + +/* + * Make this an option variable one can patch. + * But be warned: this must be a power of 2! + */ +int zstty_rbuf_size = ZSTTY_RING_SIZE; + +/* This should usually be 3/4 of ZSTTY_RING_SIZE */ +int zstty_rbuf_hiwat = (ZSTTY_RING_SIZE - (ZSTTY_RING_SIZE >> 2)); + +struct zstty_stats z8530tty_stats; + + +/* Definition of the driver for autoconfig. */ +static int zstty_match(struct device *, void *, void *); +static void zstty_attach(struct device *, struct device *, void *); + +struct cfattach zstty_ca = { + sizeof(struct zstty_softc), zstty_match, zstty_attach +}; + +struct cfdriver zstty_cd = { + NULL, "zstty", DV_TTY +}; + +struct zsops zsops_tty; + +/* Routines called from other code. */ +cdev_decl(zs); /* open, close, read, write, ioctl, stop, ... */ + +static void zsstart __P((struct tty *)); +static int zsparam __P((struct tty *, struct termios *)); +static void zs_modem __P((struct zstty_softc *zst, int onoff)); +static int zshwiflow __P((struct tty *, int)); +static void zs_hwiflow __P((struct zstty_softc *, int)); +static int zsgetbaud __P((register struct zs_chanstate *, + register int *rate, register int *tc, register u_char *rr4, + register u_char *rr11, register u_char *rr14, + register int *source, register int *sourceflag)); + +/* + * zstty_match: how is this zs channel configured? + */ +int +zstty_match(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct cfdata *cf = match; + struct zsc_attach_args *args = aux; + + /* Exact match is better than wildcard. */ + if (cf->cf_loc[0] == args->channel) + return 2; + + /* This driver accepts wildcard. */ + if (cf->cf_loc[0] == -1) + return 1; + + return 0; +} + +void +zstty_attach(parent, self, aux) + struct device *parent, *self; + void *aux; + +{ + struct zsc_softc *zsc = (void *) parent; + struct zstty_softc *zst = (void *) self; + struct zsc_attach_args *args = aux; + struct zs_chanstate *cs; + struct cfdata *cf; + struct tty *tp; + int channel, tty_unit; + dev_t dev; + + cf = zst->zst_dev.dv_cfdata; + tty_unit = zst->zst_dev.dv_unit; + channel = args->channel; + cs = &zsc->zsc_cs[channel]; + cs->cs_private = zst; + cs->cs_ops = &zsops_tty; + + zst->zst_cs = cs; + zst->zst_swflags = cf->cf_flags; /* softcar, etc. */ + zst->zst_hwflags = args->hwflags; + + zst->zst_cflag = ZSTTY_DEF_CFLAG; /* set up defaults */ + zst->zst_iflag = TTYDEF_IFLAG; /* an ioctl can change */ + zst->zst_lflag = TTYDEF_LFLAG; /* these values, modifying */ + zst->zst_oflag = TTYDEF_OFLAG; /* initial defaults */ + zst->zst_ispeed = zst->zst_ospeed = cs->cs_defspeed; + /* zst_cc set after tty is malloc'd */ + + dev = makedev(ZSTTY_MAJOR, tty_unit); + + if (zst->zst_swflags) + printf(" flags 0x%x", zst->zst_swflags); + + if (zst->zst_hwflags & ZS_HWFLAG_CONSOLE) + printf(" (console)"); + else { +#ifdef KGDB + /* + * Allow kgdb to "take over" this port. If this port is + * NOT the kgdb port, zs_check_kgdb() will return zero. + * If it IS the kgdb port, it will print "kgdb,...\n" + * and then return non-zero. + */ + if (zs_check_kgdb(cs, dev)) { + /* + * This is the kgdb port (exclusive use) + * so skip the normal attach code. + */ + return; + } +#endif + } + + tp = ttymalloc(); + tp->t_dev = dev; + tp->t_oproc = zsstart; + tp->t_param = zsparam; + tp->t_hwiflow = zshwiflow; + ttychars(tp); + bcopy(tp->t_cc, zst->zst_cc, sizeof(tp->t_cc)); + + zst->zst_tty = tp; + zst->zst_rbhiwat = zstty_rbuf_size; /* impossible value */ + zst->zst_ringmask = zstty_rbuf_size - 1; + zst->zst_rbuf = malloc(zstty_rbuf_size * sizeof(zst->zst_rbuf[0]), + M_DEVBUF, M_WAITOK); + + zstty_mdattach(zsc, zst, cs, tp); /*let the md code customize stuff */ + + if (zst->zst_hwflags & (ZS_HWFLAG_IGCTS | ZS_HWFLAG_IGDCD)) { + printf("\n Ignoring "); + switch (zst->zst_hwflags & (ZS_HWFLAG_IGCTS | ZS_HWFLAG_IGDCD)) { + case ZS_HWFLAG_IGCTS: + printf("CTS line "); break; + case ZS_HWFLAG_IGDCD: + printf("DCD line "); break; + default: + printf("CTS and DCD lines "); + } + } + + printf("\n"); + /* + * Hardware init + */ + if (zst->zst_hwflags & ZS_HWFLAG_CONSOLE) { + /* This unit is the console. */ + zst->zst_swflags |= TIOCFLAG_SOFTCAR; + /* Call _param so interrupts get enabled. */ + bcopy(&zst->zst_termios, &tp->t_termios, sizeof(struct termios)); + /* copy the whole termios in as the first "first open" won't + * do it since the speed != 0 */ + cs->cs_defspeed = zs_getspeed(cs); + tp->t_ispeed = cs->cs_defspeed; + tp->t_ospeed = cs->cs_defspeed; + (void) zsparam(tp, &tp->t_termios); + } else { + /* Not the console; may need reset. */ + int reset, s; + reset = (channel == 0) ? + ZSWR9_A_RESET : ZSWR9_B_RESET; + s = splzs(); + zs_write_reg(cs, 9, reset); + splx(s); + } + + /* + * Initialize state of modem control lines (DTR). + * If softcar is set, turn on DTR now and leave it. + * otherwise, turn off DTR now, and raise in open. + * (Keeps modem from answering too early.) + */ + zs_modem(zst, (zst->zst_swflags & TIOCFLAG_SOFTCAR) ? 1 : 0); +} + + +/* + * Return pointer to our tty. + */ +struct tty * +zstty(dev) + dev_t dev; +{ + struct zstty_softc *zst; + int unit = minor(dev); + +#ifdef DIAGNOSTIC + if (unit >= zstty_cd.cd_ndevs) + panic("zstty"); +#endif + zst = zstty_cd.cd_devs[unit]; + return (zst->zst_tty); +} + + +/* + * Open a zs serial (tty) port. + */ +int +zsopen(dev, flags, mode, p) + dev_t dev; + int flags; + int mode; + struct proc *p; +{ + register struct tty *tp; + register struct zs_chanstate *cs; + struct zstty_softc *zst; + int error, s, unit; + + unit = minor(dev); + if (unit >= zstty_cd.cd_ndevs) + return (ENXIO); + zst = zstty_cd.cd_devs[unit]; + if (zst == NULL) + return (ENXIO); + tp = zst->zst_tty; + cs = zst->zst_cs; + +#ifdef ZSTTYDEBUG + zsprintf("zs_open to channel at %p\n",cs->cs_reg_csr); +#endif + + /* If KGDB took the line, then tp==NULL */ + if (tp == NULL) + return (EBUSY); + + /* It's simpler to do this up here. */ + if (((tp->t_state & (TS_ISOPEN | TS_XCLUDE)) + == (TS_ISOPEN | TS_XCLUDE)) + && (p->p_ucred->cr_uid != 0) ) + { + return (EBUSY); + } + + s = spltty(); + + if ((tp->t_state & TS_ISOPEN) == 0) { + if ((tp->t_ispeed == 0) || (zst->zst_resetdef)) { + /* First open. Executed if either the tty + * was uninitialized, or if we choose to + * reset defaults w/ each open. */ + bcopy(&zst->zst_termios, &tp->t_termios, + sizeof(struct termios)); + if (zst->zst_swflags & TIOCFLAG_CLOCAL) + tp->t_cflag |= CLOCAL; + if (zst->zst_swflags & TIOCFLAG_CRTSCTS) + tp->t_cflag |= CRTSCTS; + if (zst->zst_swflags & TIOCFLAG_MDMBUF) + tp->t_cflag |= MDMBUF; + } + (void) zsparam(tp, &tp->t_termios); + ttsetwater(tp); + /* Flush any pending input. */ + zst->zst_rbget = zst->zst_rbput; + zs_iflush(cs); /* XXX */ + /* Turn on DTR */ + zs_modem(zst, 1); + if (zst->zst_swflags & TIOCFLAG_SOFTCAR) { + tp->t_state |= TS_CARR_ON; + } + } + error = 0; + + /* Wait for carrier. */ +#ifdef ZSTTYDEBUG + zsprintf("wait for carrier...\n"); +#endif + for (;;) { + + if (zst->zst_hwflags & ZS_HWFLAG_IGDCD) { + tp->t_state |= TS_CARR_ON; + break; + } + + /* Might never get status intr if carrier already on. */ + cs->cs_rr0 = zs_read_csr(cs); + if (cs->cs_rr0 & ZSRR0_DCD) { + tp->t_state |= TS_CARR_ON; + break; + } + + if ((tp->t_state & TS_CARR_ON) || + (tp->t_cflag & CLOCAL) || + (flags & O_NONBLOCK) ) + { + break; + } + + tp->t_state |= TS_WOPEN; + error = ttysleep(tp, (caddr_t)&tp->t_rawq, + TTIPRI | PCATCH, ttopen, 0); + if (error) { + if ((tp->t_state & TS_ISOPEN) == 0) { + /* Never get here with softcar */ + zs_modem(zst, 0); + tp->t_state &= ~TS_WOPEN; + ttwakeup(tp); + } + break; + } + } + + splx(s); + +#ifdef ZSTTYDEBUG + zsprintf("...carrier %s\n", + (tp->t_state & TS_CARR_ON) ? "on" : "off"); +#endif + + if (error == 0) + error = linesw[tp->t_line].l_open(dev, tp); + + return (error); +} + +/* + * Close a zs serial port. + */ +int +zsclose(dev, flags, mode, p) + dev_t dev; + int flags; + int mode; + struct proc *p; +{ + struct zstty_softc *zst; + register struct zs_chanstate *cs; + register struct tty *tp; + int hup; + +#ifdef ZSTTYDEBUG + zsprintf("zs_close\n"); +#endif + + zst = zstty_cd.cd_devs[minor(dev)]; + cs = zst->zst_cs; + tp = zst->zst_tty; + + /* XXX This is for cons.c. */ + if ((tp->t_state & TS_ISOPEN) == 0) + return 0; + + (*linesw[tp->t_line].l_close)(tp, flags); + hup = tp->t_cflag & HUPCL; + if (zst->zst_swflags & TIOCFLAG_SOFTCAR) + hup = 0; + if (hup) { + zs_modem(zst, 0); + /* hold low for 1 second */ + (void) tsleep((caddr_t)cs, TTIPRI, ttclos, hz); + } + if (cs->cs_creg[5] & ZSWR5_BREAK) { + zs_break(cs, 0); + } + /* XXX - turn off interrupts? */ + + ttyclose(tp); + return (0); +} + +/* + * Read/write zs serial port. + */ +int +zsread(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + register struct zstty_softc *zst; + register struct tty *tp; + + zst = zstty_cd.cd_devs[minor(dev)]; + tp = zst->zst_tty; + return (linesw[tp->t_line].l_read(tp, uio, flags)); +} + +int +zswrite(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + register struct zstty_softc *zst; + register struct tty *tp; + + zst = zstty_cd.cd_devs[minor(dev)]; + tp = zst->zst_tty; + return (linesw[tp->t_line].l_write(tp, uio, flags)); +} + +#define TIOCFLAG_ALL (TIOCFLAG_SOFTCAR | TIOCFLAG_CLOCAL | \ + TIOCFLAG_CRTSCTS | TIOCFLAG_MDMBUF ) + +int +zsioctl(dev, cmd, data, flag, p) + dev_t dev; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; +{ + register struct zstty_softc *zst; + register struct zs_chanstate *cs; + register struct tty *tp; + register int error, tmp; + + zst = zstty_cd.cd_devs[minor(dev)]; + cs = zst->zst_cs; + tp = zst->zst_tty; + + 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); + error = zsmdioctl(tp, cmd, data, flag, p); + if (error >= 0) + return (error); + + switch (cmd) { + + case TIOCSBRK: + zs_break(cs, 1); + break; + + case TIOCCBRK: + zs_break(cs, 0); + break; + + case TIOCGFLAGS: + *(int *)data = zst->zst_swflags; + break; + + case TIOCSFLAGS: + error = suser(p->p_ucred, &p->p_acflag); + if (error != 0) + return (EPERM); + tmp = *(int *)data; + /* Check for random bits... */ + if (tmp & ~TIOCFLAG_ALL) + return(EINVAL); + /* Silently enforce softcar on the console. */ + if (zst->zst_hwflags & ZS_HWFLAG_CONSOLE) + tmp |= TIOCFLAG_SOFTCAR; + /* These flags take effect during open. */ + zst->zst_swflags = tmp; + break; + + case TIOCSDTR: + zs_modem(zst, 1); + break; + + case TIOCCDTR: + zs_modem(zst, 0); + break; + +#if 0 + case SetSafeOpen: + error = suser(p->p_ucred, &p->p_acflag); + if (error != 0) + return (EPERM); + zst->zst_resetdef = 1; + break; + + case ClearSafeOpen: + error = suser(p->p_ucred, &p->p_acflag); + if (error != 0) + return (EPERM); + zst->zst_resetdef = 0; + break; + + case SetOpenDefaults: + error = suser(p->p_ucred, &p->p_acflag); + if (error != 0) + return (EPERM); + bcopy(&tp->t_termios, &zst->zst_termios, \ + sizeof(struct termios)); + break; +#endif + + case TIOCMSET: + case TIOCMBIS: + case TIOCMBIC: + case TIOCMGET: + default: + return (ENOTTY); + } + return (0); +} + +/* + * Start or restart transmission. + */ +static void +zsstart(tp) + register struct tty *tp; +{ + register struct zstty_softc *zst; + register struct zs_chanstate *cs; + register int s, nch; + + zst = zstty_cd.cd_devs[minor(tp->t_dev)]; + cs = zst->zst_cs; + + s = spltty(); + + /* + * If currently active or delaying, no need to do anything. + */ + if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) + goto out; + + /* + * If under CRTSCTS hfc and halted, do nothing + */ + if (tp->t_cflag & CRTSCTS) + if (zst->zst_tx_stopped) + goto out; + + /* + * If there are sleepers, and output has drained below low + * water mark, awaken. + */ + if (tp->t_outq.c_cc <= tp->t_lowat) { + if (tp->t_state & TS_ASLEEP) { + tp->t_state &= ~TS_ASLEEP; + wakeup((caddr_t)&tp->t_outq); + } + selwakeup(&tp->t_wsel); + } + + nch = ndqb(&tp->t_outq, 0); /* XXX */ + if (nch) { + register char *p = tp->t_outq.c_cf; + + /* mark busy, enable tx done interrupts, & send first byte */ + tp->t_state |= TS_BUSY; + (void) splzs(); + + cs->cs_preg[1] |= ZSWR1_TIE; + cs->cs_creg[1] = cs->cs_preg[1]; + zs_write_reg(cs, 1, cs->cs_creg[1]); + zs_write_data(cs, *p); + zst->zst_tba = p + 1; + zst->zst_tbc = nch - 1; + } else { + /* + * Nothing to send, turn off transmit done interrupts. + * This is useful if something is doing polled output. + */ + (void) splzs(); + cs->cs_preg[1] &= ~ZSWR1_TIE; + cs->cs_creg[1] = cs->cs_preg[1]; + zs_write_reg(cs, 1, cs->cs_creg[1]); + } +out: + splx(s); +} + +/* + * Stop output, e.g., for ^S or output flush. + */ +int +zsstop(tp, flag) + struct tty *tp; + int flag; +{ + register struct zstty_softc *zst; + register struct zs_chanstate *cs; + register int s; + + zst = zstty_cd.cd_devs[minor(tp->t_dev)]; + cs = zst->zst_cs; + + s = splzs(); + if (tp->t_state & TS_BUSY) { + /* + * Device is transmitting; must stop it. + */ + zst->zst_tbc = 0; + zst->zst_heldtbc = 0; /* XXX */ + if ((tp->t_state & TS_TTSTOP) == 0) + tp->t_state |= TS_FLUSH; + } + splx(s); + return (0); +} + +#ifndef ZS_TOLERANCE +#define ZS_TOLERANCE 50 +/* 5% in tenths of a % */ +#endif + +/* + * Search through the signal sources in the channel, and + * pick the best one for the baud rate requested. Return + * a -1 if not achievable in tolerance. Otherwise ret 0 + * and fill in the values. + * + * This routine draws inspiration from the Atari port's zs.c + * driver in NetBSD1.1 which did the same type of source switching. + * Tolerance code inspired by comspeed routine in isa/com.c. + * + * By Bill Studenmund, 1996-05-12 + */ +static int +zsgetbaud(cs, rate, tc, rr4, rr11, rr14, source, sourceflag) + register struct zs_chanstate *cs; + register int *rate, *tc, *source, *sourceflag; + register u_char *rr4, *rr11, *rr14; +{ + int i, tc0, tc1, s, sf, rate0, rate1, err, tol; + + s = -1; /* no valid source yet */ + tol = ZS_TOLERANCE; + + /* + * Step through all the sources and see which one matches + * the best. A source has to match BETTER than tol to be chosen. + * Thus if two sources give the same error, the first one will be + * chosen. Also, allow for the possability that one source might run + * both the BRG and the direct divider (i.e. RTxC). + */ + for (i=0; i< cs->cs_clock_count; i++) { + if (cs->cs_clocks[i].clk <= 0) + continue; /* skip non-existant or bad clocks */ + if (cs->cs_clocks[i].flags & ZSC_BRG) { + /* check out BRG at /16 */ + tc1 = BPS_TO_TCONST(cs->cs_clocks[i].clk >> 4, rate[0]); + if (tc1 >= 0) { + rate1 = TCONST_TO_BPS(cs->cs_clocks[i].clk >> 4, tc1); + err = abs(((rate1 - rate[0])*1000)/rate[0]); + if (err < tol) { + tol = err; + s = i; + sf = cs->cs_clocks[i].flags & ~ZSC_DIV; + tc0 = tc1; + rate0 = rate1; + } + } + } + if (cs->cs_clocks[i].flags & ZSC_DIV) { + /* + * Check out either /1, /16, /32, or /64 + * Note: for /1, you'd better be using a synchronized + * clock! + */ + int b0 = cs->cs_clocks[i].clk, e0 = abs(b0-rate[0]); + int b1 = b0 >> 4, e1 = abs(b1-rate[0]); + int b2 = b1 >> 1, e2 = abs(b2-rate[0]); + int b3 = b2 >> 1, e3 = abs(b3-rate[0]); + + if (e0 < e1 && e0 < e2 && e0 < e3) { + err = e0; + rate1 = b0; + tc1 = ZSWR4_CLK_X1; + } else if (e0 > e1 && e1 < e2 && e1 < e3) { + err = e1; + rate1 = b1; + tc1 = ZSWR4_CLK_X16; + } else if (e0 > e2 && e1 > e2 && e2 < e3) { + err = e2; + rate1 = b2; + tc1 = ZSWR4_CLK_X32; + } else { + err = e3; + rate1 = b3; + tc1 = ZSWR4_CLK_X64; + } + + err = (err * 1000)/rate[0]; + if (err < tol) { + tol = err; + s = i; + sf = cs->cs_clocks[i].flags & ~ZSC_BRG; + tc0 = tc1; + rate0 = rate1; + } + } + } +#ifdef ZSTTYDEBUG + zsprintf("Checking for rate %d. Found source #%d.\n",rate[0], s); +#endif + if (s == -1) return (-1); + /* + * Now we have a source, so set it up. + */ + *source = s; + *sourceflag = sf; + rate[0] = rate0; + if (sf & ZSC_BRG) { + *rr4 = ZSWR4_CLK_X16; + *rr11= ZSWR11_RXCLK_BAUD | ZSWR11_TXCLK_BAUD; + if (sf & ZSC_PCLK) { + *rr14 = ZSWR14_BAUD_ENA | ZSWR14_BAUD_FROM_PCLK; + } else { + *rr14 = ZSWR14_BAUD_ENA; + } + *tc = tc0; + } else { + *rr4 = tc0; + if (sf & ZSC_RTXDIV) { + *rr11 = ZSWR11_RXCLK_RTXC | ZSWR11_TXCLK_RTXC; + } else { + *rr11 = ZSWR11_RXCLK_TRXC | ZSWR11_TXCLK_TRXC; + } + *rr14= 0; + *tc = 0xffff; + } +#ifdef ZSTTYDEBUG + zsprintf("Rate is %7d, tc is %7d, source no. %2d, flags %4x\n", \ + *rate, *tc, *source, *sourceflag); + zsprintf("Registers are: 4 %x, 11 %x, 14 %x\n\n", *rr4, *rr11, *rr14); +#endif + return (0); +} + +/* + * Set ZS tty parameters from termios. + * XXX - Should just copy the whole termios after + * making sure all the changes could be done. + * XXX - Only whack the UART when params change... + */ +static int +zsparam(tp, t) + register struct tty *tp; + register struct termios *t; +{ + register struct zstty_softc *zst; + register struct zs_chanstate *cs; + register int s, cflag, hfc; + u_char tmp3, tmp4, tmp5, tmp11, tmp14; + int bps, tconst, src, srcflag; + + zst = zstty_cd.cd_devs[minor(tp->t_dev)]; + cs = zst->zst_cs; + + bps = t->c_ospeed; + if (bps < 0 || (t->c_ispeed && t->c_ispeed != bps)) + return (EINVAL); + if (bps == 0) { + /* stty 0 => drop DTR and RTS */ + zs_modem(zst, 0); + return (0); + } + if (0 > zsgetbaud(cs, &bps, &tconst, &tmp4, &tmp11, &tmp14, + &src, &srcflag)) + return (EINVAL); + + tp->t_ispeed = tp->t_ospeed = bps; + cs->cs_psource = src; + cs->cs_pclk_flag = srcflag; + + cflag = t->c_cflag; + + /* + * Make sure we don't enable hfc on a signal line we're ignoring + * + * As we enable CTS interrupts only if we have CRTSCTS, this code + * also effectivly turns off ZSWR15_CTS_IE. + */ + if (zst->zst_hwflags & ZS_HWFLAG_IGDCD) + cflag &= ~MDMBUF; + if (zst->zst_hwflags & ZS_HWFLAG_IGCTS) + cflag &= ~CRTSCTS; + + tp->t_cflag = cflag; + /* + * Block interrupts so that state will not + * be altered until we are done setting it up. + */ + s = splzs(); + + /* + * Initial values in cs_preg are set before + * our attach routine is called. The master + * interrupt enable is handled by zsc.c + */ + + cs->cs_preg[12] = tconst & 255; + cs->cs_preg[13] = tconst >> 8; + + switch (cflag & CSIZE) { + case CS5: + tmp3 = ZSWR3_RX_5; + tmp5 = ZSWR5_TX_5; + break; + case CS6: + tmp3 = ZSWR3_RX_6; + tmp5 = ZSWR5_TX_6; + break; + case CS7: + tmp3 = ZSWR3_RX_7; + tmp5 = ZSWR5_TX_7; + break; + case CS8: + default: + tmp3 = ZSWR3_RX_8; + tmp5 = ZSWR5_TX_8; + break; + } + + cs->cs_preg[3] = tmp3 | ZSWR3_RX_ENABLE; + cs->cs_preg[5] = tmp5 | ZSWR5_TX_ENABLE | ZSWR5_DTR | ZSWR5_RTS; + + tmp4 |= (cflag & CSTOPB ? ZSWR4_TWOSB : ZSWR4_ONESB); + if ((cflag & PARODD) == 0) + tmp4 |= ZSWR4_EVENP; + if (cflag & PARENB) + tmp4 |= ZSWR4_PARENB; + cs->cs_preg[4] = tmp4; + + /* + * Output hardware flow control on the chip is horrendous: + * if carrier detect drops, the receiver is disabled and if + * CTS drops, the transmitter is stoped IN MID CHARACTER! + * Therefore, do not set the HFC bit, and instead use + * the status interrupts to detect CTS changes. + */ + if (cflag & CRTSCTS) { + zst->zst_rbhiwat = zstty_rbuf_hiwat; + cs->cs_preg[15] |= ZSWR15_CTS_IE; + } else { + zst->zst_rbhiwat = zstty_rbuf_size; /* impossible value */ + cs->cs_preg[15] &= ~ZSWR15_CTS_IE; + } + /* Shouldn't the zst_rbhiwat stuff be if (cflag & CHWFLOW) ? wrs */ + + /* + * Disable DCD interrupts if we've been told to ignore + * the DCD pin. Happens on mac68k because the input line for + * DCD can also be used as a clock input. + */ + if (zst->zst_hwflags & ZS_HWFLAG_IGDCD) + cs->cs_preg[15] &= ~ZSWR15_DCD_IE; + else + cs->cs_preg[15] |= ZSWR15_DCD_IE; + + /* + * now find out which line to change for input flow control. + * Important as some ports (mac68k) don't always have input + * flow control when they have output flow control (RTS actually + * controls buffers on the Xmitter output). + */ + hfc = (cflag & CRTSCTS) ? 1 : 0; + hfc |= (cflag & MDMBUF) ? 2 : 0; + + zst->zst_hwimask = zst->zst_hwimasks[hfc]; + if ((zst->zst_rx_blocked) && (zst->zst_hwimask)) + cs->cs_preg[5] &= ~zst->zst_hwimask; + /* make sure we enforce halted-ness */ + + /* XXX check for loss of output blocking if loosing hwi ability? */ + + /* + * If nothing is being transmitted, set up new current values, + * else mark them as pending. + */ + if (cs->cs_heldchange == 0) { + if (tp->t_state & TS_BUSY) { + zst->zst_heldtbc = zst->zst_tbc; + zst->zst_tbc = 0; + cs->cs_heldchange = 0xFF; + } else { + zs_loadchannelregs(cs); + } + } + splx(s); + /* check for ttstart if lost output flow control? XXX */ + return (0); +} + +/* + * Raise or lower modem control (DTR/RTS) signals. If a character is + * in transmission, the change is deferred. + */ +static void +zs_modem(zst, onoff) + struct zstty_softc *zst; + int onoff; +{ + struct zs_chanstate *cs; + struct tty *tp; + int s, bis, and; + + cs = zst->zst_cs; + tp = zst->zst_tty; + + if (onoff) { + bis = ZSWR5_DTR | ZSWR5_RTS; + and = ~0; + } else { + bis = 0; + and = ~(ZSWR5_DTR | ZSWR5_RTS); + } + s = splzs(); + cs->cs_preg[5] = (cs->cs_preg[5] | bis) & and; + if (cs->cs_heldchange == 0) { + if (tp->t_state & TS_BUSY) { + zst->zst_heldtbc = zst->zst_tbc; + zst->zst_tbc = 0; + cs->cs_heldchange = (1<<5); + } else { + cs->cs_creg[5] = cs->cs_preg[5]; + zs_write_reg(cs, 5, cs->cs_creg[5]); + } + } + splx(s); +} + +/* + * Try to block or unblock input using hardware flow-control. + * This is called by kern/tty.c if MDMBUF|CRTSCTS is set, and + * if this function returns non-zero, the TS_TBLOCK flag will + * be set or cleared according to the "stop" arg passed. + */ +int +zshwiflow(tp, stop) + struct tty *tp; + int stop; +{ + register struct zstty_softc *zst; + int s; + + zst = zstty_cd.cd_devs[minor(tp->t_dev)]; + + /* + * This loop checks to see that we can in fact control input. + * If not, then do little except tell the upper layer the truth. + */ + if (zst->zst_hwimask == 0) + if (stop) + return 0; + else + return 1; /* yes, w/o hwi we can unblock input. ;-) */ + + s = splzs(); + if (stop) { + /* + * The tty layer is asking us to block input. + * If we already did it, just return TRUE. + */ + if (zst->zst_rx_blocked) + goto out; + zst->zst_rx_blocked = 1; + z8530tty_stats.tty_block++; + } else { + /* + * The tty layer is asking us to resume input. + * The input ring is always empty by now. + */ + zst->zst_rx_blocked = 0; + z8530tty_stats.tty_unblock++; + } + zs_hwiflow(zst, stop); + out: + splx(s); + return 1; +} + +/* + * Internal version of zshwiflow + * called at splzs + */ +static void +zs_hwiflow(zst, stop) + register struct zstty_softc *zst; + int stop; +{ + register struct zs_chanstate *cs; + register struct tty *tp; + register int bis, and; + + cs = zst->zst_cs; + tp = zst->zst_tty; + + if (stop) { + /* Block input (Lower RTS) */ + bis = 0; + and = ~zst->zst_hwimask; + } else { + /* Unblock input (Raise RTS) */ + bis = zst->zst_hwimask; + and = ~0; + } + + cs->cs_preg[5] = (cs->cs_preg[5] | bis) & and; + if (cs->cs_heldchange == 0) { + if (tp->t_state & TS_BUSY) { + zst->zst_heldtbc = zst->zst_tbc; + zst->zst_tbc = 0; + cs->cs_heldchange = (1<<5); + } else { + cs->cs_creg[5] = cs->cs_preg[5]; + zs_write_reg(cs, 5, cs->cs_creg[5]); + } + } +} + + +/**************************************************************** + * Interface to the lower layer (zscc) + ****************************************************************/ + +static void zstty_rxint __P((register struct zs_chanstate *)); +static void zstty_txint __P((register struct zs_chanstate *)); +static void zstty_stint __P((register struct zs_chanstate *)); +static void zsoverrun __P((struct zstty_softc *, long *, char *)); +static void zstty_softint __P((struct zs_chanstate *)); +/* + * XXX: need to do input flow-control to avoid ring overrun. + */ + +/* + * receiver ready interrupt. + * called at splzs + */ +static void +zstty_rxint(cs) + register struct zs_chanstate *cs; +{ + register struct zstty_softc *zst; + register int cc, put, put_next, ringmask; + register u_char c, rr0, rr1; + register u_short ch_rr1; + + zst = cs->cs_private; + put = zst->zst_rbput; + ringmask = zst->zst_ringmask; + +nextchar: + + /* + * First read the status, because reading the received char + * destroys the status of this char. + */ + rr1 = zs_read_reg(cs, 1); + c = zs_read_data(cs); + ch_rr1 = (c << 8) | rr1; + + if (ch_rr1 & (ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) { + /* Clear the receive error. */ + zs_write_csr(cs, ZSWR0_RESET_ERRORS); + } + + /* XXX: Check for the stop character? */ + + zst->zst_rbuf[put] = ch_rr1; + put_next = (put + 1) & ringmask; + + /* Would overrun if increment makes (put==get). */ + if (put_next == zst->zst_rbget) { + zst->zst_rx_overrun = 1; + } else { + /* OK, really increment. */ + put = put_next; + } + + /* Keep reading until the FIFO is empty. */ + rr0 = zs_read_csr(cs); + if (rr0 & ZSRR0_RX_READY) + goto nextchar; + + /* Done reading. */ + zst->zst_rbput = put; + + /* + * If ring is getting too full, try to block input. + */ + cc = put - zst->zst_rbget; + if (cc < 0) + cc += zstty_rbuf_size; + if ((cc > zst->zst_rbhiwat) && (zst->zst_rx_blocked == 0)) { + zst->zst_rx_blocked = 1; + zs_hwiflow(zst, 1); + z8530tty_stats.ring_block++; + } + + /* Ask for softint() call. */ + cs->cs_softreq = 1; +} + +/* + * transmitter ready interrupt. (splzs) + */ +static void +zstty_txint(cs) + register struct zs_chanstate *cs; +{ + register struct zstty_softc *zst; + register int count; + + zst = cs->cs_private; + + /* + * If we suspended output for a "held" change, + * then handle that now and resume. + * Do flow-control changes ASAP. + * When the only change is for flow control, + * avoid hitting other registers, because that + * often makes the stupid zs drop input... + */ + if (cs->cs_heldchange) { + if (cs->cs_heldchange == (1<<5)) { + /* Avoid whacking the chip... */ + cs->cs_creg[5] = cs->cs_preg[5]; + zs_write_reg(cs, 5, cs->cs_creg[5]); + } else + zs_loadchannelregs(cs); + cs->cs_heldchange = 0; + count = zst->zst_heldtbc; + } else + count = zst->zst_tbc; + + /* + * If our transmit buffer still has data, + * just send the next character. + */ + if (count > 0) { + /* Send the next char. */ + zst->zst_tbc = --count; + zs_write_data(cs, *zst->zst_tba); + zst->zst_tba++; + return; + } + + zs_write_csr(cs, ZSWR0_RESET_TXINT); + + /* Ask the softint routine for more output. */ + zst->zst_tx_empty = 1; + cs->cs_softreq = 1; +} + +/* + * status change interrupt. (splzs) + */ +static void +zstty_stint(cs) + register struct zs_chanstate *cs; +{ + register struct zstty_softc *zst; + register struct tty *tp; + register u_char rr0; + + zst = cs->cs_private; + tp = zst->zst_tty; + + rr0 = zs_read_csr(cs); + zs_write_csr(cs, ZSWR0_RESET_STATUS); + + /* + * Check here for console break, so that we can abort + * even when interrupts are locking up the machine. + */ + if ((rr0 & ZSRR0_BREAK) && + (zst->zst_hwflags & ZS_HWFLAG_CONABRT)) + { + zs_abort(zst); + return; + } + + /* + * Need to handle CTS output flow control here. + * Output remains stopped as long as either the + * zst_tx_stopped or TS_TTSTOP flag is set. + * Never restart here; the softint routine will + * do that after things are ready to move. + */ + if (((rr0 & ZSRR0_CTS) == 0) && (tp->t_cflag & CRTSCTS)) { + zst->zst_tbc = 0; + zst->zst_heldtbc = 0; + zst->zst_tx_stopped = 1; + } + + cs->cs_rr0_new = rr0; + zst->zst_st_check = 1; + + /* Ask for softint() call. */ + cs->cs_softreq = 1; +} + +/* + * Print out a ring or fifo overrun error message. + */ +static void +zsoverrun(zst, ptime, what) + struct zstty_softc *zst; + long *ptime; + char *what; +{ + + if (*ptime != time.tv_sec) { + *ptime = time.tv_sec; + log(LOG_WARNING, "%s: %s overrun\n", + zst->zst_dev.dv_xname, what); + } +} + +/* + * Software interrupt. Called at zssoft + * + * The main job to be done here is to empty the input ring + * by passing its contents up to the tty layer. The ring is + * always emptied during this operation, therefore the ring + * must not be larger than the space after "high water" in + * the tty layer, or the tty layer might drop our input. + * + * Note: an "input blockage" condition is assumed to exist if + * EITHER the TS_TBLOCK flag or zst_rx_blocked flag is set. + */ +static void +zstty_softint(cs) + struct zs_chanstate *cs; +{ + register struct zstty_softc *zst; + register struct linesw *line; + register struct tty *tp; + register int get, c, s; + int ringmask, overrun; + register u_short ring_data; + register u_char rr0, delta, flag; + + zst = cs->cs_private; + tp = zst->zst_tty; + line = &linesw[tp->t_line]; + ringmask = zst->zst_ringmask; + overrun = 0; + + /* + * Raise to tty priority while servicing the ring. + */ + s = spltty(); + + if (zst->zst_rx_overrun) { + zst->zst_rx_overrun = 0; + zsoverrun(zst, &zst->zst_rotime, "ring"); + } + + /* + * Copy data from the receive ring into the tty layer. + */ + get = zst->zst_rbget; + while (get != zst->zst_rbput) { + ring_data = zst->zst_rbuf[get]; + get = (get + 1) & ringmask; + + if (ring_data & ZSRR1_DO) + overrun++; + /* low byte of ring_data is rr1 */ + c = (ring_data >> 8) & 0xff; + if (ring_data & ZSRR1_FE) + c |= TTY_FE; + if (ring_data & ZSRR1_PE) + c |= TTY_PE; + + line->l_rint(c, tp); + } + zst->zst_rbget = get; + + /* + * If the overrun flag is set now, it was set while + * copying char/status pairs from the ring, which + * means this was a hardware (fifo) overrun. + */ + if (overrun) { + zsoverrun(zst, &zst->zst_fotime, "fifo"); + } + + /* + * We have emptied the input ring. Maybe unblock input. + * Note: an "input blockage" condition is assumed to exist + * when EITHER zst_rx_blocked or the TS_TBLOCK flag is set, + * so unblock here ONLY if TS_TBLOCK has not been set. + */ + if (zst->zst_rx_blocked && ((tp->t_state & TS_TBLOCK) == 0)) { + (void) splzs(); + zst->zst_rx_blocked = 0; + zs_hwiflow(zst, 0); /* unblock input */ + z8530tty_stats.ring_unblock++; + (void) spltty(); + } + + /* + * Do any deferred work for status interrupts. + * The rr0 was saved in the h/w interrupt to + * avoid another splzs in here. + */ + if (zst->zst_st_check) { + zst->zst_st_check = 0; + + rr0 = cs->cs_rr0_new; + delta = rr0 ^ cs->cs_rr0; + cs->cs_rr0 = rr0; + if ((delta & ZSRR0_DCD) && + ~(zst->zst_hwflags & ZS_HWFLAG_IGDCD)) { + c = ((rr0 & ZSRR0_DCD) != 0); + if ((tp->t_cflag & CHWFLOW) == CHWFLOW) { + flag = 1; + tp->t_cflag &= ~MDMBUF; + } else + flag = 0; + if (line->l_modem(tp, c) == 0) + zs_modem(zst, c); + if (flag) + tp->t_cflag |= MDMBUF; + /* + * The above trick hides MDMBUF from the tty layer + * if we also have CRTSCTS; Used as mac68k takes + * the two of them as meaning do CRTSCTS with DCD + * as the hwi line. Just CRTSCTS doesn't have a + * hwi line. + */ + } + if ((delta & ZSRR0_CTS) && (tp->t_cflag & CRTSCTS)) { + /* + * Only do restart here. Stop is handled + * at the h/w interrupt level. + */ + if (rr0 & ZSRR0_CTS) { + zst->zst_tx_stopped = 0; + tp->t_state &= ~TS_TTSTOP; + (*line->l_start)(tp); + } + } + } + + if (zst->zst_tx_empty) { + zst->zst_tx_empty = 0; + tp->t_state &= ~TS_BUSY; + if (tp->t_state & TS_FLUSH) + tp->t_state &= ~TS_FLUSH; + else + ndflush(&tp->t_outq, zst->zst_tba - + (caddr_t) tp->t_outq.c_cf); + line->l_start(tp); + } + + splx(s); +} + +struct zsops zsops_tty = { + zstty_rxint, /* receive char available */ + zstty_stint, /* external/status */ + zstty_txint, /* xmit buffer empty */ + zstty_softint, /* process software interrupt */ +}; + + diff --git a/sys/arch/mac68k/dev/z8530tty.h b/sys/arch/mac68k/dev/z8530tty.h new file mode 100644 index 00000000000..b292b474edb --- /dev/null +++ b/sys/arch/mac68k/dev/z8530tty.h @@ -0,0 +1,143 @@ +/* $OpenBSD: z8530tty.h,v 1.1 1996/05/26 19:02:13 briggs Exp $ */ +/* $NetBSD: z8530tty.h,v 1.1 1996/05/18 18:54:35 briggs Exp $ */ + +/* + * Copyright (c) 1994 Gordon W. Ross + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)zs.c 8.1 (Berkeley) 7/19/93 + */ + +/* + * Zilog Z8530 Dual UART driver (tty interface) + * + * This is the "slave" driver that will be attached to + * the "zsc" driver for plain "tty" async. serial lines. + */ + + +/* + * Allow the MD var.h to override the default CFLAG so that + * console messages during boot come out with correct parity. + */ +#ifndef ZSTTY_DEF_CFLAG +#define ZSTTY_DEF_CFLAG TTYDEF_CFLAG +#endif + +/* + * How many input characters we can buffer. + * The port-specific var.h may override this. + * Note: must be a power of two! + */ +#ifndef ZSTTY_RING_SIZE +#define ZSTTY_RING_SIZE 2048 +#endif + +struct zstty_stats { + int ring_block; + int ring_unblock; + int tty_block; + int tty_unblock; +}; + +struct zstty_softc { + struct device zst_dev; /* required first: base device */ + struct tty *zst_tty; + struct zs_chanstate *zst_cs; + + int zst_hwflags; /* see z8530var.h */ + int zst_swflags; /* TIOCFLAG_SOFTCAR, ... <ttycom.h> */ + + /* + * Printing an overrun error message often takes long enough to + * cause another overrun, so we only print one per second. + */ + long zst_rotime; /* time of last ring overrun */ + long zst_fotime; /* time of last fifo overrun */ + + /* + * The receive ring buffer. + */ + int zst_rbget; /* ring buffer `get' index */ + volatile int zst_rbput; /* ring buffer `put' index */ + int zst_ringmask; + int zst_rbhiwat; + + u_short *zst_rbuf; /* rr1, data pairs */ + + /* + * The transmit byte count and address are used for pseudo-DMA + * output in the hardware interrupt code. PDMA can be suspended + * to get pending changes done; heldtbc is used for this. It can + * also be stopped for ^S; this sets TS_TTSTOP in tp->t_state. + */ + int zst_tbc; /* transmit byte count */ + caddr_t zst_tba; /* transmit buffer address */ + int zst_heldtbc; /* held tbc while xmission stopped */ + + /* Flags to communicate with zstty_softint() */ + volatile char zst_rx_blocked; + volatile char zst_rx_overrun; + volatile char zst_tx_stopped; + volatile char zst_tx_empty; + volatile char zst_st_check; + char pad[3]; + + struct termios zst_termios; /* default values for tty flags */ +#define zst_cflag zst_termios.c_cflag +#define zst_iflag zst_termios.c_iflag +#define zst_lflag zst_termios.c_lflag +#define zst_oflag zst_termios.c_oflag +#define zst_cc zst_termios.c_cc +#define zst_ispeed zst_termios.c_ispeed +#define zst_ospeed zst_termios.c_ospeed + + char zst_resetdef; /* !=0 means reset tty defs. on open */ + char zst_hwimask; /* bits to keep low for hwiflow */ + + char zst_hwimasks[4]; /* masks for hwiflow for HFC modes */ +}; + +#define ZSTTY_RAW_CFLAG (CS8 | CREAD | HUPCL ) +#define ZSTTY_RAW_IFLAG (IXANY | IMAXBEL) +#define ZSTTY_RAW_LFLAG (ECHOE|ECHOKE|ECHOCTL) +#define ZSTTY_RAW_OFLAG (ONLCR | OXTABS) +/* Above taken from looking at a tty after a stty raw */ diff --git a/sys/arch/mac68k/dev/zs.c b/sys/arch/mac68k/dev/zs.c new file mode 100644 index 00000000000..3834dde9bc5 --- /dev/null +++ b/sys/arch/mac68k/dev/zs.c @@ -0,0 +1,752 @@ +/* $OpenBSD: zs.c,v 1.1 1996/05/26 19:02:14 briggs Exp $ */ +/* $NetBSD: zs.c,v 1.2 1996/05/23 05:18:48 briggs Exp $ */ + +/* + * Copyright (c) 1995 Gordon W. Ross + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * 4. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Gordon Ross + * + * 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. + */ + +/* + * Zilog Z8530 Dual UART driver (machine-dependent part) + * + * Runs two serial lines per chip using slave drivers. + * Plain tty/async lines use the zs_async slave. + * Sun keyboard/mouse uses the zs_kbd/zs_ms slaves. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/device.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/tty.h> +#include <sys/time.h> +#include <sys/kernel.h> +#include <sys/syslog.h> + +#include <dev/cons.h> +#include "z8530reg.h" +#include <machine/z8530var.h> + +#include <machine/autoconf.h> +#include <machine/cpu.h> + +/* + * XXX: Hard code this to make console init easier... + */ +#define NZSC 1 /* XXX */ + +/* + * Define interrupt levels. + */ +#define ZSHARD_PRI 6 /* Wired on the CPU board... */ +#define ZSSOFT_PRI 3 /* Want tty pri (4) but this is OK. */ + +/* The layout of this is hardware-dependent (padding, order). */ +struct zschan { + volatile u_char zc_csr; /* ctrl,status, and indirect access */ + u_char zc_xxx0; + u_char zc_xxx1; + u_char zc_xxx2; + volatile u_char zc_data; /* data */ + u_char zc_xxx3; + u_char zc_xxx4; + u_char zc_xxx5; +}; +/* + * The zsdevice structure is not used on the mac68k port as the + * chip is wired up weird. Channel B & A are interspursed with + * the data & control bytes +struct zsdevice { + /! Yes, they are backwards. !/ + struct zschan zs_chan_b; + struct zschan zs_chan_a; +}; +*/ + +/* Saved PROM mappings */ +static char *zsaddr[NZSC]; /* See zs_init() */ +/* Flags from cninit() */ +static int zs_hwflags[NZSC][2]; +/* Default speed for each channel */ +static int zs_defspeed[NZSC][2] = { + { 9600, /* tty00 */ + 9600 }, /* tty01 */ +}; +/* console stuff */ +void *zs_conschan = 0; +int zs_consunit; +/* device that the console is attached to--if serial. */ +dev_t mac68k_zsdev; +/* Mac stuff, some vestages of old mac serial driver here */ +volatile unsigned char *sccA = 0; + +static struct zschan *zs_get_chan_addr __P((int zsc_unit, int channel)); +void zs_init __P((void)); + +static struct zschan * +zs_get_chan_addr(zsc_unit, channel) + int zsc_unit, channel; +{ + char *addr; + struct zschan *zc; + + if (zsc_unit >= NZSC) + return NULL; + addr = zsaddr[zsc_unit]; + if (addr == NULL) + return NULL; + if (channel == 0) { + zc = (struct zschan *)(addr +2); + /* handle the fact the ports are intertwined. */ + } else { + zc = (struct zschan *)(addr); + } + return (zc); +} + + +/* Find PROM mappings (for console support). */ +static int zsinited = 0; /* 0 = not, 1 = inited, not attached, 2= attached */ + +void +zs_init() +{ + if ((zsinited == 2)&&(zsaddr[0] != (char *) sccA)) + panic("Moved zs0 address after attached!"); + zsaddr[0] = (char *) sccA; + zsinited = 1; + if (zs_conschan != 0){ /* we might have moved io under the console */ + zs_conschan = zs_get_chan_addr(0, zs_consunit); + /* so recalc the console port */ + } +} + + +/* + * Even though zsparam will set up the clock multiples, etc., we + * still set them here as: 1) mice & keyboards don't use zsparam, + * and 2) the console stuff uses these defaults before device + * attach. + */ + +static u_char zs_init_reg[16] = { + 0, /* 0: CMD (reset, etc.) */ + ZSWR1_RIE | ZSWR1_TIE | ZSWR1_SIE, + 0x18 + ZSHARD_PRI, /* IVECT */ + ZSWR3_RX_8 | ZSWR3_RX_ENABLE, + ZSWR4_CLK_X16 | ZSWR4_ONESB | ZSWR4_EVENP, + ZSWR5_TX_8 | ZSWR5_TX_ENABLE, + 0, /* 6: TXSYNC/SYNCLO */ + 0, /* 7: RXSYNC/SYNCHI */ + 0, /* 8: alias for data port */ + ZSWR9_MASTER_IE, + 0, /*10: Misc. TX/RX control bits */ + ZSWR11_TXCLK_BAUD | ZSWR11_RXCLK_BAUD, + 14, /*12: BAUDLO (default=9600) */ + 0, /*13: BAUDHI (default=9600) */ + ZSWR14_BAUD_ENA, + ZSWR15_BREAK_IE | ZSWR15_DCD_IE | ZSWR15_CTS_IE, +}; + + +/**************************************************************** + * Autoconfig + ****************************************************************/ + +/* Definition of the driver for autoconfig. */ +static int zsc_match __P((struct device *, void *, void *)); +static void zsc_attach __P((struct device *, struct device *, void *)); +static int zsc_print __P((void *aux, char *name)); + +struct cfattach zsc_ca = { + sizeof(struct zsc_softc), zsc_match, zsc_attach +}; + +struct cfdriver zsc_cd = { + NULL, "zsc", DV_DULL +}; + +int zshard __P((void *)); +int zssoft __P((void *)); + + +/* + * Is the zs chip present? + */ +static int +zsc_match(parent, vcf, aux) + struct device *parent; + void *vcf; + void *aux; +{ + return 1; +} + +static int +zsc_print(aux, name) + void *aux; + char *name; +{ + struct zsc_attach_args *args = aux; + + if (name != NULL) + printf("%s: ", name); + + if (args->channel != -1) + printf(" channel %d", args->channel); + + return UNCONF; +} + +/* + * Attach a found zs. + * + * Match slave number to zs unit number, so that misconfiguration will + * not set up the keyboard as ttya, etc. + */ +static void +zsc_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct zsc_softc *zsc = (void *) self; + struct zsc_attach_args zsc_args; + volatile struct zschan *zc; + struct zs_chanstate *cs; + int zsc_unit, channel; + int reset, s, chip; + + if (!zsinited) zs_init(); + zsinited = 2; + + zsc_unit = zsc->zsc_dev.dv_unit; + + /* Make sure everything's inited ok. */ + if (zsaddr[zsc_unit] == NULL) + panic("zs_attach: zs%d not mapped\n", zsc_unit); + + /* + * Initialize software state for each channel. + */ + for (channel = 0; channel < 2; channel++) { + cs = &zsc->zsc_cs[channel]; + + zc = zs_get_chan_addr(zsc_unit, channel); + cs->cs_reg_csr = &zc->zc_csr; + cs->cs_reg_data = &zc->zc_data; + + cs->cs_channel = channel; + cs->cs_private = NULL; + cs->cs_ops = &zsops_null; + + /* Define BAUD rate clock for the MI code. */ + cs->cs_pclk_div16 = mac68k_machine.sccClkConst*2; + cs->cs_csource = 0; + cs->cs_psource = 0; + + cs->cs_defspeed = zs_defspeed[zsc_unit][channel]; + + bcopy(zs_init_reg, cs->cs_creg, 16); + bcopy(zs_init_reg, cs->cs_preg, 16); + + /* + * Clear the master interrupt enable. + * The INTENA is common to both channels, + * so just do it on the A channel. + */ + if (channel == 0) { + zs_write_reg(cs, 9, 0); + + chip = zs_checkchip(cs); + printf(" chip type %d \n",chip); + } + cs->cs_chip = chip; + + /* + * Look for a child driver for this channel. + * The child attach will setup the hardware. + */ + zsc_args.channel = channel; + zsc_args.hwflags = zs_hwflags[zsc_unit][channel]; + if (!config_found(self, (void *) &zsc_args, zsc_print)) { + /* No sub-driver. Just reset it. */ + reset = (channel == 0) ? + ZSWR9_A_RESET : ZSWR9_B_RESET; + s = splzs(); + zs_write_reg(cs, 9, reset); + splx(s); + } + } + + /* + * Set the master interrupt enable and interrupt vector. + * (common to both channels, do it on A) + */ + cs = &zsc->zsc_cs[0]; + s = splzs(); + /* interrupt vector */ + zs_write_reg(cs, 2, zs_init_reg[2]); + /* master interrupt control (enable) */ + zs_write_reg(cs, 9, zs_init_reg[9]); + splx(s); +} + +void +zstty_mdattach(zsc, zst, cs, tp) + struct zsc_softc *zsc; + struct zstty_softc *zst; + struct zs_chanstate *cs; + struct tty *tp; +{ + zst->zst_resetdef = 0; + cs->cs_clock_count = 3; /* internal + externals */ + cs->cs_cclk_flag = 0; /* Not doing anything fancy by default */ + cs->cs_pclk_flag = 0; + cs->cs_clocks[0].clk = mac68k_machine.sccClkConst*32; + cs->cs_clocks[0].flags = ZSC_RTXBRG; /* allowing divide by 16 will + melt the driver! */ + cs->cs_clocks[1].clk = 0; + cs->cs_clocks[1].flags = ZSC_RTXBRG | ZSC_RTXDIV | ZSC_VARIABLE | ZSC_EXTERN; + cs->cs_clocks[2].clk = 0; + cs->cs_clocks[2].flags = ZSC_TRXDIV | ZSC_VARIABLE; + + /* For the mac, we have rtscts = check CTS for output control, no + * input control. mdmbuf means check DCD for output, and use DTR + * for input control. mdmbuf & rtscts means use CTS for output + * control, and DTR for input control. */ + + zst->zst_hwimasks[1] = 0; + zst->zst_hwimasks[2] = ZSWR5_DTR; + zst->zst_hwimasks[3] = ZSWR5_DTR; +} + +int +zsmdioctl(tp, com, data, flag, p) + struct tty *tp; + u_long com; + caddr_t data; + int flag; + struct proc *p; +{ + return (-1); +} + +void +zsmd_setclock(cs) + struct zs_chanstate *cs; +{ + if (cs->cs_pclk_flag & ZSC_EXTERN) { + /* XXX need to set the via! */ + } +} + +int +zshard(arg) + void *arg; +{ + struct zsc_softc *zsc; + int unit, rval; +#ifdef ZSMACDEBUG + itecnputc(mac68k_zsdev, 'Z'); +#endif + + rval = 0; + unit = zsc_cd.cd_ndevs; + while (--unit >= 0) { + zsc = zsc_cd.cd_devs[unit]; + if (zsc != NULL) { + rval |= zsc_intr_hard(zsc); + } + } +#ifdef ZSMACDEBUG + itecnputc(mac68k_zsdev, '\n'); +#endif + return (rval); +} + +int zssoftpending; + +void +zsc_req_softint(zsc) + struct zsc_softc *zsc; +{ + if (zssoftpending == 0) { + /* We are at splzs here, so no need to lock. */ + zssoftpending = ZSSOFT_PRI; + /* isr_soft_request(ZSSOFT_PRI); */ + setsoftserial(); + } +} + +int +zssoft(arg) + void *arg; +{ + struct zsc_softc *zsc; + int unit; + + /* This is not the only ISR on this IPL. */ + if (zssoftpending == 0) + return (0); + + /* + * The soft intr. bit will be set by zshard only if + * the variable zssoftpending is zero. The order of + * these next two statements prevents our clearing + * the soft intr bit just after zshard has set it. + */ +/* isr_soft_clear(ZSSOFT_PRI); */ + zssoftpending = 0; + + /* Do ttya/ttyb first, because they go faster. */ + unit = zsc_cd.cd_ndevs; + while (--unit >= 0) { + zsc = zsc_cd.cd_devs[unit]; + if (zsc != NULL) { + (void) zsc_intr_soft(zsc); + } + } + return (1); +} + + +/* + * Read or write the chip with suitable delays. + */ +#define ZS_DELAY() +/* + * MacII hardware has the delay built in. No need for extra delay. :-) + */ + +u_char +zs_read_reg(cs, reg) + struct zs_chanstate *cs; + u_char reg; +{ + u_char val; + + *cs->cs_reg_csr = reg; + ZS_DELAY(); + val = *cs->cs_reg_csr; + ZS_DELAY(); + return val; +} + +void +zs_write_reg(cs, reg, val) + struct zs_chanstate *cs; + u_char reg, val; +{ + *cs->cs_reg_csr = reg; + ZS_DELAY(); + *cs->cs_reg_csr = val; + ZS_DELAY(); +} + +u_char zs_read_csr(cs) + struct zs_chanstate *cs; +{ + register u_char v; + + v = (*cs->cs_reg_csr) ^ ZSRR0_CTS; + /* make up for the fact CTS is wired backwards */ + ZS_DELAY(); + return v; +} + +u_char zs_read_data(cs) + struct zs_chanstate *cs; +{ + register u_char v; + + v = *cs->cs_reg_data; + ZS_DELAY(); + return v; +} + +void zs_write_csr(cs, val) + struct zs_chanstate *cs; + u_char val; +{ + *cs->cs_reg_csr = val; + ZS_DELAY(); +} + +void zs_write_data(cs, val) + struct zs_chanstate *cs; + u_char val; +{ + *cs->cs_reg_data = val; + ZS_DELAY(); +} + +/**************************************************************** + * Console support functions (Originally Sun3 specific!) + * Now works w/ just mac68k port! + ****************************************************************/ + +#define zscnpollc nullcnpollc +cons_decl(zs); + +static void zs_putc __P((register volatile struct zschan *, int)); +static int zs_getc __P((register volatile struct zschan *)); +static void zscnsetup __P((void)); +extern int zsopen __P(( dev_t dev, int flags, int mode, struct proc *p)); + +/* + * Console functions. + */ + +/* + * This code modled after the zs_setparam routine in zskgdb + * It sets the console unit to a known state so we can output + * correctly. + */ +static void +zscnsetup() +{ + struct zs_chanstate cs; + struct zschan *zc; + int tconst, s; + + /* Setup temporary chanstate. */ + bzero((caddr_t)&cs, sizeof(cs)); + zc = zs_conschan; + cs.cs_reg_csr = &zc->zc_csr; + cs.cs_reg_data = &zc->zc_data; + cs.cs_channel = zs_consunit; + + bcopy(zs_init_reg, cs.cs_preg, 16); + tconst = BPS_TO_TCONST(mac68k_machine.sccClkConst*2, zs_defspeed[0][zs_consunit]); + cs.cs_preg[5] |= ZSWR5_DTR | ZSWR5_RTS; + cs.cs_preg[1] = 0; /* don't enable interrupts */ + cs.cs_preg[12] = tconst; + cs.cs_preg[13] = tconst >> 8; + + s = splhigh(); + zs_loadchannelregs(&cs); + splx(s); +} + +/* + * zscnprobe is the routine which gets called as the kernel is trying to + * figure out where the console should be. Each io driver which might + * be the console (as defined in mac68k/conf.c) gets probed. The probe + * fills in the consdev structure. Important parts are the device #, + * and the console priority. Values are CN_DEAD (don't touch me), + * CN_NORMAL (I'm here, but elsewhere might be better), CN_INTERNAL + * (the video, better than CN_NORMAL), and CN_REMOTE (pick me!) + * + * As the mac's a bit different, we do extra work here. We mainly check + * to see if we have serial echo going on, and if the tty's are supposed + * to default to raw or not. + */ +void +zscnprobe(struct consdev * cp) +{ + extern u_long IOBase; + int maj, unit; + + for (maj = 0; maj < nchrdev; maj++) { + if (cdevsw[maj].d_open == zsopen) { + break; + } + } + if (maj == nchrdev) { + /* no console entry for us */ + if (mac68k_machine.serial_boot_echo) { + mac68k_set_io_offsets(IOBase); + zs_conschan = (struct zschan *) -1; /* dummy flag for zs_init() */ + zs_consunit = 1; + zs_hwflags[0][zs_consunit] = ZS_HWFLAG_CONSOLE; + zs_init(); + zscnsetup(); + } + return; + } + + cp->cn_pri = CN_NORMAL; /* Lower than CN_INTERNAL */ + if (mac68k_machine.serial_console != 0) { + cp->cn_pri = CN_REMOTE; /* Higher than CN_INTERNAL */ + mac68k_machine.serial_boot_echo =0; + } + + unit = (mac68k_machine.serial_console == 1) ? 0 : 1; + zs_consunit = unit; + + mac68k_zsdev = cp->cn_dev = makedev(maj, unit); + + if (mac68k_machine.serial_boot_echo) { + /* + * at this point, we know that we don't have a serial + * console, but are doing echo + */ + mac68k_set_io_offsets(IOBase); + zs_conschan = (struct zschan *) -1; /* dummy flag for zs_init() */ + zs_consunit = 1; + zs_hwflags[0][zs_consunit] = ZS_HWFLAG_CONSOLE; + zs_init(); + zscnsetup(); + } + return; +} + +void +zscninit(struct consdev * cp) +{ + extern u_long IOBase; + int chan = minor(cp->cn_dev & 1); + + mac68k_set_io_offsets(IOBase); + zs_conschan = (struct zschan *) -1; + zs_consunit = chan; + zs_hwflags[0][zs_consunit] = ZS_HWFLAG_CONSOLE | ZS_HWFLAG_CONABRT; + zs_init(); + /* + * zsinit will set up the addresses of the scc. It will also, if + * zs_conschan != 0, calculate the new address of the conschan for + * unit zs_consunit. So zs_init implicitly sets zs_conschan to the right + * number. :-) + */ + zscnsetup(); + printf("\nNetBSD/mac68k console\n"); +} + + +/* + * Polled input char. + */ +static int +zs_getc(zc) + register volatile struct zschan *zc; +{ + register int s, c, rr0; + + s = splhigh(); + /* Wait for a character to arrive. */ + do { + rr0 = zc->zc_csr; + ZS_DELAY(); + } while ((rr0 & ZSRR0_RX_READY) == 0); + + c = zc->zc_data; + ZS_DELAY(); + splx(s); + + /* + * This is used by the kd driver to read scan codes, + * so don't translate '\r' ==> '\n' here... + */ + return (c); +} + +/* + * Polled output char. + */ +static void +zs_putc(zc, c) + register volatile struct zschan *zc; + int c; +{ + register int s, rr0; + register long wait = 0; + + s = splhigh(); + /* Wait for transmitter to become ready. */ + do { + rr0 = zc->zc_csr; + ZS_DELAY(); + } while (((rr0 & ZSRR0_TX_READY) == 0) && (wait++ < 1000000)); + + if ((rr0 & ZSRR0_TX_READY) != 0) { + zc->zc_data = c; + ZS_DELAY(); + } + splx(s); +} + + +/* + * Polled console input putchar. + */ +int +zscngetc(dev) + dev_t dev; +{ + register volatile struct zschan *zc = zs_conschan; + register int c; + + c = zs_getc(zc); + return (c); +} + +/* + * Polled console output putchar. + */ +void +zscnputc(dev, c) + dev_t dev; + int c; +{ + register volatile struct zschan *zc = zs_conschan; + + zs_putc(zc, c); +} + + + +/* + * Handle user request to enter kernel debugger. + */ +void +zs_abort(zst) + register struct zstty_softc *zst; +{ + register volatile struct zschan *zc = zs_conschan; + int rr0; + register long wait = 0; + + /* Wait for end of break to avoid PROM abort. */ + /* XXX - Limit the wait? */ + do { + rr0 = zc->zc_csr; + ZS_DELAY(); + } while ((rr0 & ZSRR0_BREAK) && (wait++ < ZSABORT_DELAY)); + + if (wait > ZSABORT_DELAY) { + if (zst != NULL) zst->zst_hwflags &= ~ZS_HWFLAG_CONABRT; + /* If we time out, turn off the abort ability! */ + } + + /* XXX - Always available, but may be the PROM monitor. */ + Debugger(); +} |