diff options
author | Miod Vallat <miod@cvs.openbsd.org> | 2004-11-25 18:32:12 +0000 |
---|---|---|
committer | Miod Vallat <miod@cvs.openbsd.org> | 2004-11-25 18:32:12 +0000 |
commit | 06601d07c3f3a948ad4b435e9cce75bf9077f455 (patch) | |
tree | 9251a36ae3d19312fbc860cddc0107339e4181c8 /sys/arch/mac68k/dev/zs.c | |
parent | ab3b10696322bc78ecdd88ea3d7b1c61c975f342 (diff) |
Move towards a more flexible and generic interrupt system, as well as better
zs behaviour.
From NetBSD; integration work by Martin Reindl
Diffstat (limited to 'sys/arch/mac68k/dev/zs.c')
-rw-r--r-- | sys/arch/mac68k/dev/zs.c | 905 |
1 files changed, 615 insertions, 290 deletions
diff --git a/sys/arch/mac68k/dev/zs.c b/sys/arch/mac68k/dev/zs.c index ab82e6d875c..e8f6f83416b 100644 --- a/sys/arch/mac68k/dev/zs.c +++ b/sys/arch/mac68k/dev/zs.c @@ -1,7 +1,8 @@ -/* $OpenBSD: zs.c,v 1.15 2004/03/09 21:46:47 xsa Exp $ */ -/* $NetBSD: zs.c,v 1.12 1996/12/18 05:04:22 scottr Exp $ */ +/* $OpenBSD: zs.c,v 1.16 2004/11/25 18:32:10 miod Exp $ */ +/* $NetBSD: zs.c,v 1.19 1998/01/12 19:22:18 thorpej Exp $ */ /* + * Copyright (c) 1996-1998 Bill Studenmund * Copyright (c) 1995 Gordon W. Ross * All rights reserved. * @@ -37,6 +38,19 @@ * 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. + * Other ports use their own mice & keyboard slaves. + * + * Credits & history: + * + * With NetBSD 1.1, port-mac68k started using a port of the port-sparc + * (port-sun3?) zs.c driver (which was in turn based on code in the + * Berkeley 4.4 Lite release). Bill Studenmund did the port, with + * help from Allen Briggs and Gordon Ross <gwr@netbsd.org>. Noud de + * Brouwer field-tested the driver at a local ISP. + * + * Bill Studenmund and Gordon Ross then ported the machine-independant + * z8530 driver to work with port-mac68k. NetBSD 1.2 contained an + * intermediate version (mac68k using a local, patched version of */ #include <sys/param.h> @@ -52,47 +66,62 @@ #include <sys/syslog.h> #include <dev/cons.h> -#include "z8530reg.h" + +#include <mac68k/dev/z8530reg.h> #include <machine/z8530var.h> #include <machine/autoconf.h> #include <machine/cpu.h> +#include <machine/psc.h> #include <machine/viareg.h> +#ifdef DDB #include <ddb/db_var.h> +#endif + +/* Are these in a header file anywhere? */ +/* Booter flags interface */ +#define ZSMAC_RAW 0x01 +#define ZSMAC_LOCALTALK 0x02 +#define ZS_STD_BRG (57600*4) + +#include "zsc.h" /* get the # of zs chips defined */ /* - * XXX: Hard code this to make console init easier... + * Some warts needed by z8530tty.c - */ -#define NZSC 1 /* XXX */ +int zs_def_cflag = (CREAD | CS8 | HUPCL); +int zs_major = 12; + +/* + * abort detection on console will now timeout after iterating on a loop + * the following # of times. Cheep hack. Also, abort detection is turned + * off after a timeout (i.e. maybe there's not a terminal hooked up). + */ +#define ZSABORT_DELAY 3000000 /* * Define interrupt levels. */ -#define ZSHARD_PRI 6 /* Wired on the CPU board... */ -#define ZSSOFT_PRI 3 /* Want tty pri (4) but this is OK. */ +#define ZSHARD_PRI 4 /* Wired on the CPU board... */ +/* + * Serial port cards with zs chips on them are actually at the + * NuBus interrupt level, which is lower than 4. But blocking + * level 4 interrupts will block those interrupts too, so level + * 4 is fine. + */ /* 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; + u_char zc_xxx1; /* part of the other channel lives here!*/ + u_char zc_xxx2; /* Yea Apple! */ 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 interspersed 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() */ @@ -103,16 +132,19 @@ 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. */ +void *zs_conschan = 0; +int zs_consunit; +/* device to which the console is attached--if serial. */ dev_t mac68k_zsdev; -/* Mac stuff, some vestages of old mac serial driver here */ +/* Mac stuff */ volatile unsigned char *sccA = 0; +int nzsc_attached = 0; /* needed as long as we have spurious + * interupt problems. + */ static struct zschan *zs_get_chan_addr(int zsc_unit, int channel); void zs_init(void); +int zs_cn_check_speed(int bps); static struct zschan * zs_get_chan_addr(zsc_unit, channel) @@ -137,7 +169,7 @@ zs_get_chan_addr(zsc_unit, channel) /* Find PROM mappings (for console support). */ -static int zsinited = 0; /* 0 = not, 1 = inited, not attached, 2= attached */ +int zsinited = 0; /* 0 = not, 1 = inited, not attached, 2= attached */ void zs_init() @@ -187,7 +219,7 @@ static u_char zs_init_reg[16] = { /* Definition of the driver for autoconfig. */ static int zsc_match(struct device *, void *, void *); static void zsc_attach(struct device *, struct device *, void *); -static int zsc_print(void *aux, const char *name); +static int zsc_print(void *, const char *name); struct cfattach zsc_ca = { sizeof(struct zsc_softc), zsc_match, zsc_attach @@ -197,8 +229,8 @@ struct cfdriver zsc_cd = { NULL, "zsc", DV_DULL }; -int zshard(void *); -int zssoft(void *); +int zshard(void *); +int zssoft(void *); /* @@ -213,22 +245,6 @@ zsc_match(parent, vcf, aux) return 1; } -static int -zsc_print(aux, name) - void *aux; - const char *name; -{ - struct zsc_attach_args *args = aux; - - if (name != NULL) - printf("%s: ", name); - - if (args->channel != -1) - printf(" channel %d", args->channel); - - return UNCONF; -} - /* * Attach a found zs. * @@ -244,12 +260,13 @@ zsc_attach(parent, self, aux) struct zsc_softc *zsc = (void *) self; struct zsc_attach_args zsc_args; volatile struct zschan *zc; + struct xzs_chanstate *xcs; struct zs_chanstate *cs; int zsc_unit, channel; - int reset, s; - int chip = 0; /* XXX quiet bogus gcc warning */ + int s, chip, theflags; - if (!zsinited) zs_init(); + if (!zsinited) + zs_init(); zsinited = 2; zsc_unit = zsc->zsc_dev.dv_unit; @@ -258,52 +275,138 @@ zsc_attach(parent, self, aux) if (zsaddr[zsc_unit] == NULL) panic("zs_attach: zs%d not mapped", zsc_unit); + chip = 0; +#ifdef DEBUG + printf(" chip type %d",chip); +#endif + printf("\n"); + /* * 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; + zsc_args.channel = channel; + zsc_args.hwflags = zs_hwflags[zsc_unit][channel]; + xcs = &zsc->xzsc_xcs_store[channel]; + cs = &xcs->xzs_cs; + zsc->zsc_cs[channel] = cs; 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; + zc = zs_get_chan_addr(zsc_unit, channel); + cs->cs_reg_csr = &zc->zc_csr; + cs->cs_reg_data = &zc->zc_data; - cs->cs_defspeed = zs_defspeed[zsc_unit][channel]; + if (channel == 0) /* Double check interupts are off */ + zs_write_reg(cs, 9, 0); 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. - */ + /* Current BAUD rate generator clock. */ + cs->cs_brg_clk = ZS_STD_BRG; /* RTxC is 230400*16, so use 230400 */ + cs->cs_defspeed = zs_defspeed[zsc_unit][channel]; + cs->cs_defcflag = zs_def_cflag; + + /* Make these correspond to cs_defcflag (-crtscts) */ + cs->cs_rr0_dcd = ZSRR0_DCD; + cs->cs_rr0_cts = 0; + cs->cs_wr5_dtr = ZSWR5_DTR; + cs->cs_wr5_rts = 0; + +#ifdef __notyet__ + cs->cs_slave_type = ZS_SLAVE_NONE; +#endif + + /* Define BAUD rate stuff. */ + xcs->cs_clocks[0].clk = ZS_STD_BRG * 16; + xcs->cs_clocks[0].flags = ZSC_RTXBRG; + xcs->cs_clocks[1].flags = + ZSC_RTXBRG | ZSC_RTXDIV | ZSC_VARIABLE | ZSC_EXTERN; + xcs->cs_clocks[2].flags = ZSC_TRXDIV | ZSC_VARIABLE; + xcs->cs_clock_count = 3; + if (channel == 0) { - zs_write_reg(cs, 9, 0); + theflags = mac68k_machine.modem_flags; + xcs->cs_clocks[1].clk = mac68k_machine.modem_dcd_clk; + xcs->cs_clocks[2].clk = mac68k_machine.modem_cts_clk; + } else { + theflags = mac68k_machine.print_flags; + xcs->cs_clocks[1].flags = ZSC_VARIABLE; + /* + * Yes, we aren't defining ANY clock source enables for + * the printer's DCD clock in. The hardware won't let + * us use it. But a clock will freak out the chip, so + * we let you set it, telling us to bar interrupts on + * the line. + */ + xcs->cs_clocks[1].clk = mac68k_machine.print_dcd_clk; + xcs->cs_clocks[2].clk = mac68k_machine.print_cts_clk; + } + if (xcs->cs_clocks[1].clk) + zsc_args.hwflags |= ZS_HWFLAG_NO_DCD; + if (xcs->cs_clocks[2].clk) + zsc_args.hwflags |= ZS_HWFLAG_NO_CTS; + +#ifdef DEBUG + printf("zsc%d channel %d: d_speed %6d DCD clk %ld CTS clk %ld", + zsc_unit, channel, cs->cs_defspeed, + xcs->cs_clocks[1].clk, xcs->cs_clocks[2].clk); +#endif + + /* Set defaults in our "extended" chanstate. */ + xcs->cs_csource = 0; + xcs->cs_psource = 0; + xcs->cs_cclk_flag = 0; /* Nothing fancy by default */ + xcs->cs_pclk_flag = 0; + + if (theflags & ZSMAC_RAW) { + zsc_args.hwflags |= ZS_HWFLAG_RAW; +#ifdef DEBUG + printf(" (raw defaults)"); +#endif + } - chip = 0; /* We'll turn chip checking on post 1.2 */ - printf(" chip type %d \n",chip); + /* + * XXX - This might be better done with a "stub" driver + * (to replace zstty) that ignores LocalTalk for now. + */ + if (theflags & ZSMAC_LOCALTALK) { + printf(" shielding from LocalTalk"); + cs->cs_defspeed = 1; + cs->cs_creg[ZSRR_BAUDLO] = cs->cs_preg[ZSRR_BAUDLO] = 0xff; + cs->cs_creg[ZSRR_BAUDHI] = cs->cs_preg[ZSRR_BAUDHI] = 0xff; + zs_write_reg(cs, ZSRR_BAUDLO, 0xff); + zs_write_reg(cs, ZSRR_BAUDHI, 0xff); + /* + * If we might have LocalTalk, then make sure we have + * the Baud rate low-enough to not do any damage. + */ } - cs->cs_chip = chip; + + /* + * We used to disable chip interrupts here, but we now + * do that in zscnprobe, just in case MacOS left the chip on. + */ + + xcs->cs_chip = chip; + + /* Stash away a copy of the final H/W flags. */ + xcs->cs_hwflags = zsc_args.hwflags; + +#ifdef DEBUG + printf("\n"); +#endif /* * 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)) { + if (!config_found(self, (void *)&zsc_args, zsc_print)) { /* No sub-driver. Just reset it. */ - reset = (channel == 0) ? + u_char reset = (channel == 0) ? ZSWR9_A_RESET : ZSWR9_B_RESET; s = splzs(); zs_write_reg(cs, 9, reset); @@ -311,161 +414,111 @@ zsc_attach(parent, self, aux) } } + if (current_mac_model->class == MACH_CLASSAV) { + add_psc_lev4_intr(2, zshard, zsc); + add_psc_lev4_intr(3, zshard, zsc); + } else { + intr_establish(zshard, zsc, ZSHARD_PRI); + } + + /* XXX - Now safe to install interrupt handlers. */ + /* * Set the master interrupt enable and interrupt vector. * (common to both channels, do it on A) */ - cs = &zsc->zsc_cs[0]; + 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]); + nzsc_attached++; splx(s); } -void -zstty_mdattach(zsc, zst, cs, tp) - struct zsc_softc *zsc; - struct zstty_softc *zst; - struct zs_chanstate *cs; - struct tty *tp; +static int +zsc_print(aux, name) + void *aux; + const char *name; { - int theflags; - - 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].flags = ZSC_RTXBRG | ZSC_RTXDIV | ZSC_VARIABLE | ZSC_EXTERN; - cs->cs_clocks[2].flags = ZSC_TRXDIV | ZSC_VARIABLE; - if (zst->zst_dev.dv_unit == 0) { - theflags = mac68k_machine.modem_flags; - cs->cs_clocks[1].clk = mac68k_machine.modem_dcd_clk; - cs->cs_clocks[2].clk = mac68k_machine.modem_cts_clk; - } else if (zst->zst_dev.dv_unit == 1) { - theflags = mac68k_machine.print_flags; - cs->cs_clocks[1].flags = ZSC_VARIABLE; - /* - * Yes, we aren't defining ANY clock source enables for the - * printer's DCD clock in. The hardware won't let us - * use it. But a clock will freak out the chip, so we - * let you set it, telling us to bar interrupts on the line. - */ - cs->cs_clocks[1].clk = mac68k_machine.print_dcd_clk; - cs->cs_clocks[2].clk = mac68k_machine.print_cts_clk; - } else - theflags = 0; - - if (cs->cs_clocks[1].clk) - zst->zst_hwflags |= ZS_HWFLAG_IGDCD; - if (cs->cs_clocks[2].clk) - zst->zst_hwflags |= ZS_HWFLAG_IGCTS; - - if (theflags & ZSMAC_RAW) { - zst->zst_cflag = ZSTTY_RAW_CFLAG; - zst->zst_iflag = ZSTTY_RAW_IFLAG; - zst->zst_lflag = ZSTTY_RAW_LFLAG; - zst->zst_oflag = ZSTTY_RAW_OFLAG; - printf(" (raw defaults)"); - } - if (theflags & ZSMAC_LOCALTALK) { - printf(" shielding from LocalTalk"); - zst->zst_ospeed = tp->t_ospeed = 1; - zst->zst_ispeed = tp->t_ispeed = 1; - cs->cs_defspeed = 1; - cs->cs_creg[ZSRR_BAUDLO] = cs->cs_preg[ZSRR_BAUDLO] = 0xff; - cs->cs_creg[ZSRR_BAUDHI] = cs->cs_preg[ZSRR_BAUDHI] = 0xff; - zs_write_reg(cs, ZSRR_BAUDLO, 0xff); - zs_write_reg(cs, ZSRR_BAUDHI, 0xff); - /* - * If we might have LocalTalk, then make sure we have the - * Baud rate low-enough to not do any damage. - */ - } + struct zsc_attach_args *args = aux; - /* 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. */ + if (name != NULL) + printf("%s: ", name); - zst->zst_hwimasks[1] = 0; - zst->zst_hwimasks[2] = ZSWR5_DTR; - zst->zst_hwimasks[3] = ZSWR5_DTR; + if (args->channel != -1) + printf(" channel %d", args->channel); + return UNCONF; } int -zsmdioctl(tp, com, data, flag, p) - struct tty *tp; - u_long com; +zsmdioctl(cs, cmd, data) + struct zs_chanstate *cs; + u_long cmd; caddr_t data; - int flag; - struct proc *p; { - return (-1); + switch (cmd) { + default: + return (-1); + } + return (0); } void zsmd_setclock(cs) struct zs_chanstate *cs; { + struct xzs_chanstate *xcs = (void *)cs; + if (cs->cs_channel != 0) return; + /* * If the new clock has the external bit set, then select the * external source. */ - via_set_modem((cs->cs_pclk_flag & ZSC_EXTERN) ? 1 : 0); + via_set_modem((xcs->cs_pclk_flag & ZSC_EXTERN) ? 1 : 0); } +static int zssoftpending; + +/* + * Do the minimum work to pull data off of the chip and queue it up + * for later processing. + */ 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); -} + struct zsc_softc *zsc = (struct zsc_softc *)arg; + int rval; -int zssoftpending; + if (zsc == NULL) + return 0; -void -zsc_req_softint(zsc) - struct zsc_softc *zsc; -{ - if (zssoftpending == 0) { + rval = 0; + rval |= zsc_intr_hard(zsc); + if ((zsc->zsc_cs[0]->cs_softreq) || (zsc->zsc_cs[1]->cs_softreq)) { + /* zsc_req_softint(zsc); */ /* We are at splzs here, so no need to lock. */ - zssoftpending = ZSSOFT_PRI; - /* isr_soft_request(ZSSOFT_PRI); */ - setsoftserial(); + if (zssoftpending == 0) { + zssoftpending = 1; + setsoftserial(); + } } + return (rval); } +/* + * Similar scheme as for zshard (look at all of them) + */ int zssoft(arg) void *arg; { - struct zsc_softc *zsc; - int unit; + register struct zsc_softc *zsc; + register int unit; /* This is not the only ISR on this IPL. */ if (zssoftpending == 0) @@ -473,32 +526,290 @@ zssoft(arg) /* * 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. + * the variable zssoftpending is zero. */ -/* isr_soft_clear(ZSSOFT_PRI); */ zssoftpending = 0; - /* Do ttya/ttyb first, because they go faster. */ - unit = zsc_cd.cd_ndevs; - while (--unit >= 0) { + for (unit = 0; unit < zsc_cd.cd_ndevs; ++unit) { zsc = zsc_cd.cd_devs[unit]; - if (zsc != NULL) { - (void) zsc_intr_soft(zsc); - } + if (zsc == NULL) + continue; + (void) zsc_intr_soft(zsc); } return (1); } +#ifndef ZS_TOLERANCE +#define ZS_TOLERANCE 51 +/* 5% in tenths of a %, plus 1 so that exactly 5% will be ok. */ +#endif + /* - * Read or write the chip with suitable delays. + * check out a rate for acceptability from the internal clock + * source. Used in console config to validate a requested + * default speed. Placed here so that all the speed checking code is + * in one place. + * + * != 0 means ok. */ -#define ZS_DELAY() +int +zs_cn_check_speed(bps) + int bps; /* target rate */ +{ + int tc, rate; + + tc = BPS_TO_TCONST(ZS_STD_BRG, bps); + if (tc < 0) + return 0; + rate = TCONST_TO_BPS(ZS_STD_BRG, tc); + if (ZS_TOLERANCE > abs(((rate - bps)*1000)/bps)) + return 1; + else + return 0; +} + /* - * MacII hardware has the delay built in. No need for extra delay. :-) + * 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 return 0 + * and fill in the values. + * + * This routine draws inspiration from the Atari port's zs.c + * driver in NetBSD 1.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 */ +int +zs_set_speed(cs, bps) + struct zs_chanstate *cs; + int bps; /* bits per second */ +{ + struct xzs_chanstate *xcs = (void *) cs; + int i, tc, tc0 = 0, tc1, s, sf = 0; + int src, rate0, rate1, err, tol; + + if (bps == 0) + return (0); + + src = -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 < xcs->cs_clock_count; i++) { + if (xcs->cs_clocks[i].clk <= 0) + continue; /* skip non-existant or bad clocks */ + if (xcs->cs_clocks[i].flags & ZSC_BRG) { + /* check out BRG at /16 */ + tc1 = BPS_TO_TCONST(xcs->cs_clocks[i].clk >> 4, bps); + if (tc1 >= 0) { + rate1 = TCONST_TO_BPS(xcs->cs_clocks[i].clk >> 4, tc1); + err = abs(((rate1 - bps)*1000)/bps); + if (err < tol) { + tol = err; + src = i; + sf = xcs->cs_clocks[i].flags & ~ZSC_DIV; tc0 = tc1; + rate0 = rate1; + } + } + } + if (xcs->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 = xcs->cs_clocks[i].clk, e0 = abs(b0-bps); + int b1 = b0 >> 4, e1 = abs(b1-bps); + int b2 = b1 >> 1, e2 = abs(b2-bps); + int b3 = b2 >> 1, e3 = abs(b3-bps); + + 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)/bps; + if (err < tol) { + tol = err; + src = i; + sf = xcs->cs_clocks[i].flags & ~ZSC_BRG; + tc0 = tc1; + rate0 = rate1; + } + } + } +#ifdef ZSMACDEBUG + zsprintf("Checking for rate %d. Found source #%d.\n",bps, src); +#endif + if (src == -1) + return (EINVAL); /* no can do */ + + /* + * The M.I. layer likes to keep cs_brg_clk current, even though + * we are the only ones who should be touching the BRG's rate. + * + * Note: we are assuming that any ZSC_EXTERN signal source comes in + * on the RTxC pin. Correct for the mac68k obio zsc. + */ + if (sf & ZSC_EXTERN) + cs->cs_brg_clk = xcs->cs_clocks[i].clk >> 4; + else + cs->cs_brg_clk = ZS_STD_BRG; + + /* + * Now we have a source, so set it up. + */ + s = splzs(); + xcs->cs_psource = src; + xcs->cs_pclk_flag = sf; + bps = rate0; + if (sf & ZSC_BRG) { + cs->cs_preg[4] = ZSWR4_CLK_X16; + cs->cs_preg[11]= ZSWR11_RXCLK_BAUD | ZSWR11_TXCLK_BAUD; + if (sf & ZSC_PCLK) { + cs->cs_preg[14] = ZSWR14_BAUD_ENA | ZSWR14_BAUD_FROM_PCLK; + } else { + cs->cs_preg[14] = ZSWR14_BAUD_ENA; + } + tc = tc0; + } else { + cs->cs_preg[4] = tc0; + if (sf & ZSC_RTXDIV) { + cs->cs_preg[11] = ZSWR11_RXCLK_RTXC | ZSWR11_TXCLK_RTXC; } else { + cs->cs_preg[11] = ZSWR11_RXCLK_TRXC | ZSWR11_TXCLK_TRXC; } + cs->cs_preg[14]= 0; + tc = 0xffff; + } + /* Set the BAUD rate divisor. */ + cs->cs_preg[12] = tc; + cs->cs_preg[13] = tc >> 8; + splx(s); + +#ifdef ZSMACDEBUG + zsprintf("Rate is %7d, tc is %7d, source no. %2d, flags %4x\n", \ + bps, tc, src, sf); + zsprintf("Registers are: 4 %x, 11 %x, 14 %x\n\n", + cs->cs_preg[4], cs->cs_preg[11], cs->cs_preg[14]); +#endif + + cs->cs_preg[5] |= ZSWR5_RTS; /* Make sure the drivers are on! */ + + /* Caller will stuff the pending registers. */ + return (0); +} + +int +zs_set_modes(cs, cflag) + struct zs_chanstate *cs; + int cflag; /* bits per second */ +{ + struct xzs_chanstate *xcs = (void*)cs; + int s; + + /* + * Make sure we don't enable hfc on a signal line we're ignoring. + * As we enable CTS interrupts only if we have CRTSCTS or CDTRCTS, + * this code also effectivly turns off ZSWR15_CTS_IE. + * + * Also, 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. (Just set CLOCAL.) + * + * If someone tries to turn an invalid flow mode on, Just Say No + * (Suggested by gwr) + */ +#if 0 + if ((cflag & CDTRCTS) && (cflag & (CRTSCTS | MDMBUF))) + return (EINVAL); +#endif + if (xcs->cs_hwflags & ZS_HWFLAG_NO_DCD) { + if (cflag & MDMBUF) + return (EINVAL); + cflag |= CLOCAL; + } +#if 0 + if ((xcs->cs_hwflags & ZS_HWFLAG_NO_CTS) && (cflag & (CRTSCTS | CDTRCTS))) + return (EINVAL); +#endif + + /* + * Output hardware flow control on the chip is horrendous: + * if carrier detect drops, the receiver is disabled, and if + * CTS drops, the transmitter is stoped IN MID CHARACTER! + * Therefore, NEVER set the HFC bit, and instead use the + * status interrupt to detect CTS changes. + */ + s = splzs(); + if ((cflag & (CLOCAL | MDMBUF)) != 0) + cs->cs_rr0_dcd = 0; + else + cs->cs_rr0_dcd = ZSRR0_DCD; + /* + * The mac hardware only has one output, DTR (HSKo in Mac + * parlance). In HFC mode, we use it for the functions + * typically served by RTS and DTR on other ports, so we + * have to fake the upper layer out some. + * + * CRTSCTS we use CTS as an input which tells us when to shut up. + * We make no effort to shut up the other side of the connection. + * DTR is used to hang up the modem. + * + * In CDTRCTS, we use CTS to tell us to stop, but we use DTR to + * shut up the other side. + */ + if ((cflag & CRTSCTS) != 0){ + cs->cs_wr5_dtr = ZSWR5_DTR; + cs->cs_wr5_rts = 0; + cs->cs_rr0_cts = ZSRR0_CTS; +#if 0 + } else if ((cflag & CDTRCTS) != 0) { + cs->cs_wr5_dtr = 0; + cs->cs_wr5_rts = ZSWR5_DTR; + cs->cs_rr0_cts = ZSRR0_CTS; +#endif + } else if ((cflag & MDMBUF) != 0) { + cs->cs_wr5_dtr = 0; + cs->cs_wr5_rts = ZSWR5_DTR; + cs->cs_rr0_cts = ZSRR0_DCD; + } else { + cs->cs_wr5_dtr = ZSWR5_DTR; + cs->cs_wr5_rts = 0; + cs->cs_rr0_cts = 0; + } + splx(s); + + /* Caller will stuff the pending registers. */ + return(0); +} + +/* + * Read or write the chip with suitable delays. + * MacII hardware has the delay built in. + * No need for extra delay. :-) However, some clock-chiped + * macs, or zsc's on serial add-on boards might need it. + */ +#define ZS_DELAY() u_char zs_read_reg(cs, reg) @@ -528,30 +839,32 @@ zs_write_reg(cs, reg, val) u_char zs_read_csr(cs) struct zs_chanstate *cs; { - register u_char v; + u_char val; - v = (*cs->cs_reg_csr) ^ ZSRR0_CTS; - /* make up for the fact CTS is wired backwards */ + val = *cs->cs_reg_csr; ZS_DELAY(); - return v; + /* make up for the fact CTS is wired backwards */ + val ^= ZSRR0_CTS; + return val; } -u_char zs_read_data(cs) +void zs_write_csr(cs, val) struct zs_chanstate *cs; + register u_char val; { - register u_char v; - - v = *cs->cs_reg_data; + /* Note, the csr does not write CTS... */ + *cs->cs_reg_csr = val; ZS_DELAY(); - return v; } -void zs_write_csr(cs, val) +u_char zs_read_data(cs) struct zs_chanstate *cs; - u_char val; { - *cs->cs_reg_csr = val; + register u_char val; + + val = *cs->cs_reg_data; ZS_DELAY(); + return val; } void zs_write_data(cs, val) @@ -563,8 +876,11 @@ void zs_write_data(cs, val) } /**************************************************************** - * Console support functions (Originally Sun3 specific!) - * Now works w/ just mac68k port! + * Console support functions (mac68k specific!) + * Note: this code is allowed to know about the layout of + * the chip registers, and uses that to keep things simple. + * XXX - I think I like the mvme167 code better. -gwr + * XXX - Well :-P :-) -wrs ****************************************************************/ #define zscnpollc nullcnpollc @@ -587,27 +903,38 @@ extern int zsopen( dev_t dev, int flags, int mode, struct proc *p); static void zscnsetup() { - struct zs_chanstate cs; + struct xzs_chanstate xcs; + struct zs_chanstate *cs; struct zschan *zc; int tconst, s; /* Setup temporary chanstate. */ - bzero((caddr_t)&cs, sizeof(cs)); + bzero((caddr_t)&xcs, sizeof(xcs)); + cs = &xcs.xzs_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); + cs->cs_reg_csr = &zc->zc_csr; + cs->cs_reg_data = &zc->zc_data; + cs->cs_channel = zs_consunit; + cs->cs_brg_clk = ZS_STD_BRG; + + bcopy(zs_init_reg, cs->cs_preg, 16); + cs->cs_preg[5] |= ZSWR5_DTR | ZSWR5_RTS; + cs->cs_preg[15] = ZSWR15_BREAK_IE; + tconst = BPS_TO_TCONST(cs->cs_brg_clk, + zs_defspeed[0][zs_consunit]); + cs->cs_preg[12] = tconst; + cs->cs_preg[13] = tconst >> 8; + /* can't use zs_set_speed as we haven't set up the + * signal sources, and it's not worth it for now + */ + + /* + * As zs_loadchannelregs doesn't touch reg 9 (interupt control), + * we won't accidentally turn on interupts below + */ + s = splhigh(); + zs_loadchannelregs(cs); + splx(s); } /* @@ -620,81 +947,85 @@ zscnsetup() * (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. + * to see if we have serial echo going on. Also chould check for default + * speeds. */ 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() */ + extern u_long IOBase; + int maj, unit, i; + + for (maj = 0; maj < nchrdev; maj++) { + if (cdevsw[maj].d_open == zsopen) { + break; + } + } + if (maj != nchrdev) { + 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; + zs_conschan = (struct zschan *) -1; /* dummy flag for zs_init() */ + + 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 + */ + 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; + } + + if ((i = mac68k_machine.modem_d_speed) > 0) { + if (zs_cn_check_speed(i)) + zs_defspeed[0][0] = i; + } + if ((i = mac68k_machine.print_d_speed) > 0) { + if (zs_cn_check_speed(i)) + zs_defspeed[0][1] = i; + } + mac68k_set_io_offsets(IOBase); + 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 if we are (or think we are) going to use the + * chip for console I/O, we just set up the internal addresses for it. + * + * Now turn off interrupts for the chip. Note: this code piece is the + * only vestage of the NetBSD 1.0 ser driver. :-) + */ + unit = sccA[2]; /* reset reg. access */ + unit = sccA[0]; + sccA[2] = 9; sccA[2] = 0; /* write 0 to reg. 9, clearing MIE */ + sccA[2] = ZSWR0_CLR_INTR; unit = sccA[2]; /* reset any pending ints. */ + sccA[0] = ZSWR0_CLR_INTR; unit = sccA[0]; + + if (mac68k_machine.serial_boot_echo) + 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; -#ifdef ZS_CONSOLE_ABORT - zs_hwflags[0][zs_consunit] |= ZS_HWFLAG_CONABRT; -#endif - 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("\nOpenBSD/mac68k console\n"); + */ + zscnsetup(); + printf("\nOpenBSD/mac68k console\n"); } @@ -784,27 +1115,21 @@ zscnputc(dev, c) * Handle user request to enter kernel debugger. */ void -zs_abort(zst) - register struct zstty_softc *zst; +zs_abort(cs) + struct zs_chanstate *cs; { - register volatile struct zschan *zc = zs_conschan; + volatile struct zschan *zc = zs_conschan; int rr0; register long wait = 0; - if ((zst->zst_hwflags & ZS_HWFLAG_CONABRT) == 0) - return; - /* 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! */ - } + if (wait > ZSABORT_DELAY) + return; /* XXX */ #ifdef DDB if (db_console) Debugger(); |