From 347304eb41682f0aee3b92cd961c4d31c4b08180 Mon Sep 17 00:00:00 2001 From: briggs Date: Sat, 11 Feb 1995 19:06:57 +0000 Subject: [PATCH] Many changes and updates from Dave Leonard (d@fnarg.net.au) and Brad Parker (brad@fcr.com). I've been sitting on these for a while. Notes from Dave: redocumented z8530 stuff. Added break ioctls and detection. Pass framing/parity errors to line discipline. Added TIOC[SG]FLAG. Attempt at bringing all chip ops together. deepended s/w fifos to match chip's. --- sys/arch/mac68k/dev/ser.c | 1379 +++++++++++++++++++++++++--------- sys/arch/mac68k/dev/serreg.h | 269 +++++-- 2 files changed, 1210 insertions(+), 438 deletions(-) diff --git a/sys/arch/mac68k/dev/ser.c b/sys/arch/mac68k/dev/ser.c index b1b7ea914b30..5d2c1a90f7ce 100644 --- a/sys/arch/mac68k/dev/ser.c +++ b/sys/arch/mac68k/dev/ser.c @@ -1,4 +1,4 @@ -/* $NetBSD: ser.c,v 1.16 1994/11/29 03:38:48 briggs Exp $ */ +/* $NetBSD: ser.c,v 1.17 1995/02/11 19:06:57 briggs Exp $ */ /* * Copyright (c) 1982, 1986, 1990 The Regents of the University of California. @@ -66,16 +66,87 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * Hacked by Brad Parker, - * added CTS input flow control - * added DCD event detection - * added software fifo's + * Further disgusting kludges by Dave Leonard, + * redocumented z8530 stuff + * added break ioctls and detection + * attempt at bringing all chip ops together + * deepened s/w fifos to match chip's + * added TIOC[SG]FLAG + * farming/parity errors passed to discipline * - * Mac II serial device interface + * Hacked by Brad Parker, + * added CTS input flow control + * added DCD event detection + * added software fifo's * - * Information used in this source was gleaned from low-memory - * global variables in MacOS and the Advanced Micro Devices - * 1992 Data Book/Handbook. + * Mac II serial device interface + * + * Information used in this source was gleaned from low-memory + * global variables in MacOS and the Advanced Micro Devices + * 1992 Data Book/Handbook. + * + * Also from the Zilog Scc User's manual for the z85x30 + * + *-- + * + * Notes on complying with appletalk (not yet in this module) + # + * unlike system 7 which requires a reboot when you change the + * usage of a port from serial to appletalk, it should be possible + * to re-configure the chip on the fly (just like old System 6s) + * + * This driver should be augmented to provide LLAP. + * + * To quote from a Zilog manual: + * "A majority of the difficult timing and all of the hardware interface + * problems crop up in the LLAP driver. These problems are so difficult + * that it makes sense to start writing such a driver by writing + * experimental routines that transmit and recieve frames." + * The manual then goes on to provide discussion and code for a z80181 + * based implementation. + * + * LLAP uses csma/ca and implementations with zilog's SCC put it into + * SDLC (synchronous) mode. ie each frame starts with a 01111110 sequence + * (which is why you sometimes see ~'s when localtalk kicks in on a + * dumb terminal) The frame ends with a sequence of 12 to 18 1's which + * the SCC interprets as a Abort sequence and loses sync. This is handy + * because you can prepare your packet then wait for the abort interrupt + * to send it. + * + * A faster way gets you to pulse the RTS line for at least one bit time + * and then idle it for at least 2 bit times to check for collision + * before sending. "This syncronisation is obtained by first enabling + * the hardware line so that an edge is detected by all the recievers on + * the network." It looks like the start of a clocking period. The + * other SCCs see the idle period shortly after and "assume the clock + * has been lost (missing clock bit set on RR10)". Zilog dont have an + * interrupt on RR10, so it must be polled. :( + * + * LLAP nodes have a dynamically allocated node ID. ie you get a random + * one then check to see if anyone else has got it by sending a lapENQ + * and if no-one winges withing 200 usec, then its yours. (You have to + * send a good couple of lapENQs in case the packet was lost, or the + * winging node was busy, etc) Node ID 0xFF is a broadcast address. + * + * The 200usec and stuff can be timed by the chip by setting a counter + * and getting an interrupt when it reaches zero. + * + * LLAP packets are made up of: + * + * 1 dest ID + * 1 source ID + * 1 LLAP type { 0x0 ... 0x7f, lapENQ, lapACK, lapRTS, lapCTS} + * 0-600 data + * 1 CRC (TxUnderrun Int.) + * 1 CRC + * 1 flag + * + * Init string template for LLAP: + * 4=0x20 1=0x00 2=0x00 3=0xcc 5=0x60 6=0x00 7=0x7e 8=0x01 + * 10=0xe0 11=0xf6 12=0x06 13=0x0 14=0x60 14=0xc0 14=0xa0 + * 14=0x20 14=0x01 3=0xcc 15=0x0 16=0x10 1=0x00 9=0x09 + * (go through this with a fine comb) + * */ #include "ser.h" @@ -97,66 +168,230 @@ #include -/*#define DEBUG*/ #undef DEBUG +/* #define DEBUG /**/ + +/*#define use_hw_flow*/ /* attempt to use hw flow control -buggy */ volatile unsigned char *sccA = (unsigned char *) 0x4000; -static void serstart __P((register struct tty *)); -static int serparam __P((register struct tty *, register struct termios *)); -static int serctl __P((dev_t dev, int bits, int how)); -extern int ser_intr __P((void)); +static void serstart __P((register struct tty *)); +static int serparam __P((register struct tty *, register struct termios *)); +static int serctl __P((dev_t dev, int bits, int how)); +extern int ser_intr __P((void)); -static int ser_active = 0; -static int nser = NSER; -static int serdefaultrate = TTYDEF_SPEED; +static int nser = NSER; +static int serdefaultrate = TTYDEF_SPEED; -struct tty *ser_tty[NSER]; +struct tty *ser_tty[NSER]; -extern struct tty *constty; +extern struct tty *constty; -#define UNIT(x) minor(x) +#define UNIT(x) minor(x) +/* + * This'll end up in a header file before (next) Christmas + */ + +/* + * This structure is persistent across open/closes and is + * zeroed only at init time - it is reliable as the current state + * of the serial channel. + */ + +#define SER_WRITEREG(unit,reg,bits) \ + ser_status[unit].wr[reg]=(bits),\ + SER_DOCNTL(unit,reg,bits) + +#define SERCTL_DTR 0x001 /* DTR */ +#define SERCTL_RTS 0x002 /* RTS */ +#define SERCTL_AUTO 0x004 /* chip level hardware flow control */ +#define SERCTL_DCD 0x008 /* DCD [readonly] */ +#define SERCTL_BRK 0x010 /* raise break */ +#define SERCTL_CTS 0x020 /* CTS [readonly] */ +#define SERCTL_BUSY 0x040 /* tx in progress [readonly] */ +#define SERCTL_INHIB 0X080 /* hardware inhibit of transmitter */ +#define SERCTL_INBRK 0x100 /* break recv'd before this char [ro]*/ + +/* writable bits */ +#define SERCTL_MASK ( /* bits that can be user modified */ \ + SERCTL_DTR| \ + SERCTL_RTS| \ + SERCTL_AUTO| \ + SERCTL_BRK| \ + SERCTL_INHIB| \ + 0) +/* + * NOTE: by some magical coincidence, SERCTL_{DCD,RTS} match up with + * SER_R0_{DCD,CTS}!!! This feature isn't used yet; but helps in debugging. + */ + +volatile struct ser_status { - unsigned char ddcd, dcts; /* delta (change) flags */ - unsigned char dcd, cts; /* last state of signal */ - unsigned char dtr, rts; /* current state of signal */ - int oflo; /* s/w fifo over flow */ - int over; /* h/w fifo over flow */ - int flags; -#define SER_BUSY 0x01 + unsigned char wr[16]; /* last register writes */ + + unsigned char ddcd, dcts; /* delta (change) flags */ + int oflo; /* s/w fifo over flow */ + int over; /* h/w fifo over flow */ + unsigned char intpend; /* number of sw ints pending */ + + int state; /* (intended|derived) state of chip */ } ser_status[NSER]; -#define SCC_INT 10 -#define SCC_SPEED 11 +#define SCC_INT 10 +#define SCC_SPEED 11 + +/* + * End of stuff for header file. + */ + +#ifdef DEBUG +/* + * This is one big printf that dumps all the interesting + * info about a serial port + */ + +/* DBG used to restrict debugging to a particular serial port */ +#define DBG(u) ((u)==0)/**/ + +/* fwd decl */ +static ser_apply_state(int); + +static void +ser_dbg( char*msg, int unit ) +{ + volatile struct ser_status *s = &ser_status[unit]; + struct tty *t = ser_tty[unit]; + + if (!DBG(unit)) return; + + if (!t) { /* sanity */ + printf("ser%d: %s (no attached tty)\n",unit,msg); + return; + } + + printf( "ser%d: %s %s%s" + " %s=(%s%s%s%s%s%s%s%s%s)" /*ser*/ + " %s=(%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s)" /*tty*/ + " %s=(%s%s%s%s%s%s%s%s%s%s%s%s%s%s)" /*cflag*/ + "\n", + unit,msg, + + s->oflo?"oflo ":"", s->over?"over ":"", + + "ser", + s->state&SERCTL_BUSY?"busy ":"", + s->state&SERCTL_INHIB?"txinhib ":"", + s->state&SERCTL_INBRK?"inbrk ":"", + s->state&SERCTL_DTR?"DTR ":"", + s->state&SERCTL_RTS?"RTS ":"", + s->state&SERCTL_BRK?"BRK ":"", + s->state&SERCTL_DCD?"DCD ":"", + s->state&SERCTL_CTS?"CTS ":"", + s->state&SERCTL_AUTO?"AUTO ":"", + + "tty", + t->t_state&TS_ASLEEP ?"ASLEEP ":"", + t->t_state&TS_ASYNC ?"ASYNC ":"", + t->t_state&TS_BUSY ?"BUSY ":"", + t->t_state&TS_CARR_ON ?"CARR_ON ":"", + t->t_state&TS_FLUSH ?"FLUSH ":"", + t->t_state&TS_ISOPEN ?"ISOPEN ":"", + t->t_state&TS_TBLOCK ?"TBLOCK ":"", + t->t_state&TS_TIMEOUT ?"TIMEOUT ":"", + t->t_state&TS_TTSTOP ?"TTSTOP ":"", + t->t_state&TS_WOPEN ?"WOPEN ":"", + t->t_state&TS_XCLUDE ?"XCLUDE ":"", + t->t_state&TS_BKSL ?"BKSL ":"", + t->t_state&TS_CNTTB ?"CNTTB ":"", + t->t_state&TS_ERASE ?"ERASE ":"", + t->t_state&TS_LNCH ?"LNCH ":"", + t->t_state&TS_TYPEN ?"TYPEN ":"", + t->t_state&TS_LOCAL ?"LOCAL ":"", + + "cflag", + t->t_cflag&CIGNORE ?"CIGNORE ":"", + (t->t_cflag&CSIZE)==CS5?"CS5 ":"", + (t->t_cflag&CSIZE)==CS6?"CS6 ":"", + (t->t_cflag&CSIZE)==CS7?"CS7 ":"", + (t->t_cflag&CSIZE)==CS8?"CS8 ":"", + t->t_cflag&CSTOPB ?"CSTOPB ":"", + t->t_cflag&CREAD ?"CREAD ":"", + t->t_cflag&PARENB ?"PARENB ":"", + t->t_cflag&PARODD ?"PARODD ":"", + t->t_cflag&HUPCL ?"HUPCL ":"", + t->t_cflag&CLOCAL ?"CLOCAL ":"", + t->t_cflag&CRTSCTS ?"CRTSCTS ":"", + t->t_cflag&MDMBUF ?"MDMBUF ":"", + t->t_cflag&CHWFLOW ?"CHWFLOW ":"", + 0 ); + +} +#else +#define DBG(u) 0 +#endif DEBUG /* SCC initialization string from Steve Allen (wormey@eskimo.com) */ + static unsigned char ser_init_bytes[]={ - 4, 0x44, /* Transmit/Receive control. Select Async or Sync - mode and clock multiplier. */ - 3, 0xc0, /* select receiver control. Bit d0 (rx enable) - must be set to 0 at this time. */ - 5, 0xe2, /* select transmit control. Bit d3 (tx enable) - must be set to 0 at this time. */ - 9, 0x06, /* select interrupt control. Bit d3 (mie) - must be set to 0 at this time. */ - 10, 0x00, /* miscellaneous control. */ - 11, 0x50, /* clock control. */ - 12, 0x04, /* time constant LB. */ - 13, 0x00, /* time constant HB. */ - 14, 0x00, /* miscellaneous control. Bit d0 (BR gen enable) - must be set to 0 at this time. */ - 3, 0xc1, /* set d0 (rx enable). */ - 5, 0xea, /* set d3 (tx enable). */ - 0, 0x80, /* reset txCRC. */ - 14, 0x01, /* BR gen enable. Enable DPLL. */ - 1, 0x00, /* make sure DMA not set. */ - 15, 0x00, /* disable external interrupts. */ - 0, 0x10, /* reset ext/status twice. */ - 0, 0x10, - 1, 0x0a, /* enable rcv and xmit interrupts. */ - 9, 0x0e, /* enable master interrupt bit d3. */ + + 4, SER_W4_PARNONE| /* no parity */ + SER_W4_1SBIT| /* one stop bit async */ + SER_W4_CLKX16, /* clock = 16 * data rate */ + + 3, SER_W3_RX8DBITS, /* 8 bit data rx'd */ + /* dont enable RX yet */ + + 5, /*SER_W5_DTR| /* enable DTR */ + /*SER_W5_RTS| /* enable RTS */ + SER_W5_TX8DBITS, /* 8 data bits tx'd */ + /* TX disabled */ + + 9, SER_W9_NV| /* No vector set up yet */ + SER_W9_DLC, /* disable lower chain */ + /* do not enable master interrupt (mie) yet */ + + 10, SER_W10_NRZ, /* nrz mode */ + + 11, SER_W11_TXBR| /* source of tx clock is Baud rate gen */ + SER_W11_RXBR, /* source of rx clock is baud rate gen */ + + 12, 0x04, /* time constant LB. Will be replaced below */ + 13, 0x00, /* time constant HB. Will be replaced below */ + + 14, 0x00, /* don't enable the BR yet */ + + 5, /*SER_W5_DTR|*/ + /*SER_W5_RTS|*/ + SER_W5_TX8DBITS| + SER_W5_ENBTX, /* enable tx */ + + 3, SER_W3_RX8DBITS| + SER_W3_ENBRX, /* enable rx */ + + 0, 0x80, /* reset txCRC. XXX is this needed? */ + + 14, SER_W14_ENBBR, /* enable BR. DPLL not affected */ + + 1, 0x00, /* WAIT/DMA request, disable rx int */ + + 15, SER_W15_DCDINT| /* enable DCD, CTS and Break interrupts */ + SER_W15_CTSINT| + SER_W15_BRKINT, + + 0, SER_W0_RSTESINTS, /* reset ext/status ints */ + 0, SER_W0_RSTESINTS, /* twice */ + + 0xff,0xff, /* marker before interrupts are enabled */ + + 1, SER_W1_ENBTXINT| /* enable tx intertrupts */ + SER_W1_ENBRXINT, + /* SER_W1_ENBR1INT, /* and 1st rx */ + + 9, SER_W9_NV| + SER_W9_DLC| + SER_W9_MIE, /* globally enable interrupts */ }; extern int matchbyname(); @@ -164,13 +399,15 @@ extern int matchbyname(); static void serinit(int running_interrupts) { -static int initted=0; +static int initted=0; int bcount; int i, s, spd; + int unit; /* * Will be called twice if we're running a serial console. */ + if (initted++) return; @@ -180,24 +417,38 @@ static int initted=0; s = splhigh(); - SER_DOCNTL(0, 9, 0xc0); + SER_WRITEREG(0, 9, SER_W9_HWRESET); + + /* short delay after the hardware reset */ + /* take this time to init some structs */ + + for(unit=0;unit>8) & 0xff); - if (!running_interrupts) { - if ( ser_init_bytes[i] == 0x01 - && ser_init_bytes[i+1] == 0x0a) - break; + + if (ser_init_bytes[i]==0xff) { + if (!running_interrupts) break; + } else { + for(unit=0;unit= NSER ){ return (ENXIO); } - ser_active |= 1 << unit; + if (ser_tty[unit]) { tp = ser_tty[unit]; } else { @@ -263,16 +512,41 @@ seropen(dev_t dev, int flag, int mode, struct proc *p) /* serial device open code */ - bzero((char *)&ser_status[unit], sizeof(struct ser_status)); +#ifdef DEBUG + ser_dbg("seropen",unit); +#endif + /* + * set up the initial (unlatched) values of cts rts + * assumes that the port is quiescent until interrupts enabled + */ + + SER_DOCNTL(unit, 15, + ser_status[unit].wr[15] & ~(SER_W15_DCDINT|SER_W15_CTSINT) ); + reg0 = SER_STATUS(unit, 0); + SER_DOCNTL(unit, 15, ser_status[unit].wr[15]); + + ser_status[unit].state = 0; /* blam */ + + SER_WRITEREG(unit, 0, SER_W0_RSTESINTS); + + if (reg0 & SER_R0_DCD) { + ser_status[unit].state |= SERCTL_DCD; + tp->t_state |= TS_CARR_ON; + } + if (!/*!!?*/reg0 & SER_R0_CTS) /* kapow */ + ser_status[unit].state |= SERCTL_CTS; + + /* + * enable interrupts here + * because raising RTS/DTR later might + * trigger an external response + */ + + serctl(unit, 1, SCC_INT); /* turn on RTS & DTR */ - serctl(unit, SER_W5_RTS | SER_W5_DTR, DMSET); - if(serctl(unit, 0, DMGET) & SER_R0_DCD) - tp->t_state |= TS_CARR_ON; - - /* enable interrupts */ - serctl(unit, 1, SCC_INT); + serctl(unit, SERCTL_RTS|SERCTL_DTR, DMSET); /* end serial device open code */ @@ -280,18 +554,20 @@ seropen(dev_t dev, int flag, int mode, struct proc *p) while ((flag&O_NONBLOCK) == 0 && (tp->t_cflag&CLOCAL) == 0 && (tp->t_state & TS_CARR_ON) == 0) { tp->t_state |= TS_WOPEN; +#ifdef DEBUG + ser_dbg("seropen: sleeping",unit); +#endif if (error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH, ttopen, 0)) break; } (void) spl0(); +#ifdef DEBUG + ser_dbg("seropen: ok",unit); +#endif if (error == 0) error = (*linesw[tp->t_line].l_open)(dev, tp); -#if defined(DEBUG) - printf("ser: exiting seropen()\n"); -#endif - return (error); } @@ -303,23 +579,28 @@ serclose(dev_t dev, int flag, int mode, struct proc *p) register int unit; int s; -#if defined(DEBUG) - printf("ser: entered serclose()\n"); -#endif unit = UNIT(dev); tp = ser_tty[unit]; (*linesw[tp->t_line].l_close)(tp, flag); /* serial device close code */ - +#ifdef DEBUG + ser_dbg("serclose",unit); +#endif /* disable interrupts */ serctl(unit, 0, SCC_INT); + ser_status[unit].state &= ~SERCTL_BUSY; /* in case busy */ if (tp->t_cflag&HUPCL || tp->t_state&TS_WOPEN || (tp->t_state&TS_ISOPEN) == 0) - serctl(unit, 0, DMSET); /* turn RTS and DTR off */ + /* turn RTS and DTR off */ + serctl(unit, SERCTL_DTR|SERCTL_RTS, DMBIC); - ser_active &= ~(1 << unit); + /* ser_active &= ~(1 << unit); */ + +#ifdef DEBUG + ser_dbg("serclose: closed",unit); +#endif /* end of serial device close code */ ttyclose(tp); @@ -328,9 +609,6 @@ serclose(dev_t dev, int flag, int mode, struct proc *p) ser_tty[unit] = NULL; #endif -#if defined(DEBUG) - printf("ser: exiting serclose()\n"); -#endif return (0); } @@ -341,9 +619,6 @@ serread(dev, uio, flag) int flag; { register struct tty *tp = ser_tty[UNIT(dev)]; -#if defined(DEBUG) - printf("ser: called serread()\n"); -#endif return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } @@ -357,16 +632,13 @@ serwrite(dev, uio, flag) int unit = UNIT(dev); register struct tty *tp = ser_tty[unit]; -#if defined(DEBUG) - printf("ser: called serwrite()\n"); -#endif return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } /* private buffers used by driver at splscc() */ -#define INBUFLEN 128 -#define OUTBUFLEN 512 -static unsigned char ser_inbuf[NSER][INBUFLEN]; +#define INBUFLEN 256 +#define OUTBUFLEN 512 +static unsigned int ser_inbuf[NSER][INBUFLEN]; /* was unsigned char[] */ static volatile unsigned char ser_inlen[NSER] = {0,0}; static unsigned char ser_intail[NSER] = {0,0}; @@ -385,95 +657,287 @@ static volatile unsigned int ser_outtail[NSER] = {0,0}; extern int ser_intr(void) { - /* serial interrupt code */ - unsigned char reg0, reg1, ch, ch1, c, bits; - int s; - register int unit; + /* serial interrupt code */ +#define HWFIFO 3 /* depth of the hardware fifo */ + unsigned char r0o, reg0, reg1, reg2, reg3, r1[HWFIFO], c,cdpth; + unsigned int ch[HWFIFO]; + int s,reset_ext=0; + volatile int *state; + register int unit; - /* read status to reset SCC state machine */ - reg0 = SCCCNTL(0); + /* read status to reset SCC state machine */ - /* reset port B vector to see who interrupted us */ - bits = SER_STATUS(1, 2) & 0x0e; - if (bits < 8) - unit = 1; - else - unit = 0; + r0o = SCCCNTL(0); - reg0 = SER_STATUS(unit, 0); - switch ((bits & 7) >> 1) { - case 0: /* tranmitter buffer empty */ - if (ser_outlen[unit] > 0) - { - c = ser_outbuf[unit][ser_outtail[unit]]; - ser_outtail[unit] = (ser_outtail[unit] + 1) % OUTBUFLEN; - SCCRDWR(unit) = c; - ser_outlen[unit]--; - } else { - SER_DOCNTL(unit, 0, SER_W0_RSTTXPND); - ser_status[unit].flags &= ~SER_BUSY; - setsoftserial(); - } - SER_DOCNTL(unit, 0, SER_W0_RSTIUS); - break; - case 1: /* ext/status change */ - if ((reg0 & SER_R0_DCD) && ser_status[unit].dcd == 0) - ser_status[unit].ddcd = 1; - else - if (!(reg0 & SER_R0_DCD) && ser_status[unit].dcd != 0) - ser_status[unit].ddcd = 1; - ser_status[unit].dcd = reg0 & SER_R0_DCD; + /* port B's vector contains info on the interrupt */ - if ((reg0 & SER_R0_CTS) && ser_status[unit].cts == 0) - ser_status[unit].dcts = 1; - else - if (!(reg0 & SER_R0_CTS) && ser_status[unit].cts != 0) - ser_status[unit].dcts = 1; - ser_status[unit].cts = reg0 & SER_R0_CTS; + reg2 = SER_STATUS(1, 2); - if (reg0 & SER_R0_TXUNDERRUN) - SER_DOCNTL(unit, 0, SER_W0_RSTTXUNDERRUN); - - SER_DOCNTL(unit, 0, SER_W0_RSTESINTS); - SER_DOCNTL(unit, 0, SER_W0_RSTIUS); - break; - case 2: /* recv char available */ - ch = SCCRDWR(unit); - c = 1; - if (SER_STATUS(unit, 0) & SER_R0_RXREADY) { - ch1 = SCCRDWR(unit); - c = 2; + switch (reg2 & SER_R2_CHANMASK) { + case SER_R2_CHANA: unit=0; break;/* channel that caused int */ + case SER_R2_CHANB: unit=1; break; } - if (ser_inlen[unit] < INBUFLEN) - ser_inbuf[unit][(ser_intail[unit] + (ser_inlen[unit]++)) % INBUFLEN] = ch; - else ser_status[unit].oflo++; - if (c > 1) { - if (ser_inlen[unit] < INBUFLEN) - ser_inbuf[unit][(ser_intail[unit] + (ser_inlen[unit]++)) % INBUFLEN] = ch1; - else ser_status[unit].oflo++; + reg0 = SER_STATUS(unit, 0); + +#ifdef DEBUG + /*if (DBG(unit))printf("%02x%02x.",reg0,reg2);*/ +#endif + + state = &ser_status[unit].state; /* optimisation */ + + switch (reg2 & SER_R2_MASK) { + + case SER_R2_TX: /* tranmitter buffer empty */ + + if (ser_outlen[unit] == 0) { + SER_WRITEREG(unit, 0, SER_W0_RSTTXPND); + *state &= ~SERCTL_BUSY; + setsoftserial(); + ser_status[unit].intpend++; + } else { + c = ser_outbuf[unit][ser_outtail[unit]]; + ser_outtail[unit] = + (ser_outtail[unit] + 1) % OUTBUFLEN; + SCCRDWR(unit) = c; + ser_outlen[unit]--; + } + + /* + * Flow control - the Zilog way(TM) + * + * The zilog 85x30 has a magic feature called + * 'auto enable' which, in short, allows the CTS pin + * to enable/disable the TX line. However, in Zilog's + * infinite wisdom, 'auto enable' also forces you to + * have DCD {en,dis}able the RX. This is bad if you are + * in CLOCAL mode and are using CRTSCTS. + * + * The safest thing to do is just fall through to + * status check when we are doing flow control and + * there's an ext/change int pending - we use magic + * there... + */ + + if ( ser_tty[unit] && ser_tty[unit]->t_cflag&CRTSCTS) { + reg3 = SER_STATUS(1, 3); + if ( reg3 & (SER_R3_AIPES|SER_R3_BIPES) ) + goto check_status; + } + + break; + + case SER_R2_EXTCHG: /* ext/status change */ +check_status: + reset_ext++; + + /* cts */ + + /* XXX why???? + * for some bizarre reason, cts seems to be inverted + * this might be because the interrupt latches the + * previous value of the line? then why not for DCD? + */ + + if ( ! ((reg0^*state)&SER_R0_CTS)) { + + *state ^= SERCTL_CTS; + +#ifdef DEBUG + if (DBG(unit)) + printf("c%c",*state&SERCTL_CTS?'+':'-'); +#endif + if (*state & SERCTL_CTS) { + + /* cts is now on */ + + /* + * we turn on the txinhibit if needed + * here it can be turned off at an + * upper level + */ + if ( +#ifdef use_hw_flow + /* not on auto, but */ + !(*state&SERCTL_AUTO) && +#endif use_hw_flow + ser_tty[unit] && + ser_tty[unit]->t_cflag&CRTSCTS + /* doing flow control */ + ) { + serctl( unit, SERCTL_INHIB, + DMBIS ); /* blam */ + } + } else { + /* cts is now off */ + + /* we were inhibiting */ + if (*state & SERCTL_INHIB) { + *state&=~SERCTL_INHIB; + SER_DOCNTL( unit, 5, + ser_status[unit].wr[5] |= SER_W5_ENBTX ); + /* but not any more */ + } + } + ser_status[unit].dcts = 1; + } + + /* dcd */ + + if ( (!(reg0&SER_R0_DCD) && (*state&SERCTL_DCD)) + || ( (reg0&SER_R0_DCD) && !(*state&SERCTL_DCD))) { + +#ifdef DEBUG + if (DBG(unit)) + printf("d%c",reg0&SER_R0_DCD?'+':'-'); +#endif + + ser_status[unit].ddcd = 1; /* flag delta */ + + if (reg0&SER_R0_DCD) { + /* + * we have DCD + */ + + *state |= SERCTL_DCD; +#ifdef use_hw_flow + /* + * ... and need flow control + */ + if ( ser_tty[unit] + && ser_tty[unit]->t_cflag&CRTSCTS) { + /* TOO SLOW (but more understandable) */ + /* + serctl( unit, SERCTL_AUTO, DMBIS ); + serctl( unit, SERCTL_INHIB, DMBIC ); + */ + /* use the force, luke */ + *state &= ~SERCTL_INHIB; /* take that you scoundrel! */ + *state |= SERCTL_AUTO; /* and that! */ /* pow */ + SER_DOCNTL(unit,3,ser_status[unit].wr[3]|=SER_W3_AUTOEN); + SER_DOCNTL(unit,5,ser_status[unit].wr[5]&=~SER_W5_ENBTX); + /* blam */ + } +#endif use_hw_flow + } else { + /* + * we don't have DCD + */ + *state &= ~SERCTL_DCD; +#ifdef use_hw_flow + if (*state & SERCTL_AUTO) /* but, omigosh we need auto */ + { + /* we need to switch-over to manual inhibit */ + if (!(*state & SERCTL_CTS)){/* we have no cts */ + *state |= SERCTL_INHIB; /* so we need to inhibit tx */ + SER_DOCNTL(unit,5, + ser_status[unit].wr[5]|=SER_W5_ENBTX); + } + *state &= ~ SERCTL_AUTO; /* no more auto */ + SER_DOCNTL(unit,3,ser_status[unit].wr[3]&=~SER_W3_AUTOEN); + } +#endif use_hw_flow + } + } + + /* tx underrun */ + + if (reg0 & SER_R0_TXUNDERRUN) + SER_WRITEREG(unit, 0, SER_W0_RSTTXUNDERRUN); + + /* break */ + + if (reg0 & SER_R0_BREAK) { + /* + * breaks are now enqueued onto the receive queue + * and turned into a null with a framing error. + * XXX This seems to be broken XXX + * + * a point to note is that ext/stat interrupts are lower + * priority than tx or rx (at the scc level), but + * this should be okay + */ +#ifdef DEBUG + /*if (DBG(unit))*/ + printf("b"); +#endif + /* break leaves a null in the input fifo - this will be + * picked up when the break ends and sent to the disc */ + + *state |= SERCTL_INBRK; /* flag so that next rx knows is a brk */ + + } + + setsoftserial(); + ser_status[unit].intpend++; + + break; + + case SER_R2_RX: /* recv char available */ + + /* fast read of scc's hardware fifo + * this is actually a race between the cpu and the scc hardware + * that the cpu will most likely win + * + * We'll get an rxoverrun if our s/w buffer fills up first :( + */ + + for(c=0;ct_state & TS_ISOPEN) && (tp->t_flags & CRTSCTS)){ + if (ser_status[unit].state & SERCTL_CTS) { + tp->t_state &= ~TS_TTSTOP; + + /* + * next 3 lines not needed when AUTO flow is on, + * but it doesn't hurt + */ + serctl(unit, SERCTL_INHIB, DMBIC ); + + serstart(tp); + } else { + tp->t_state |= TS_TTSTOP; + } + } +#ifdef DEBUG + ser_dbg("cts",unit); +#endif + } + + /* + * check for overflows + */ if (ser_status[unit].oflo || ser_status[unit].over) { + int ooflo; s = splhigh(); + ooflo = ser_status[unit].oflo; ser_status[unit].oflo = 0; ser_status[unit].over = 0; splx(s); + /* where's _our_ flow control? */ + printf("ser%d: silo overflow, oflo = %d\n",unit,ooflo); if (tp->t_state & TS_ISOPEN) (*linesw[tp->t_line].l_rint)('#', tp); } - /* check for change in DCD */ + + /* + * check for change in DCD + */ + if (ser_status[unit].ddcd) { + s = splhigh(); ser_status[unit].ddcd = 0; splx(s); - if (0) { - if (ser_status[unit].dcd) - tp->t_state |= TS_CARR_ON; - else - tp->t_state &= ~TS_CARR_ON; - (*linesw[tp->t_line].l_modem)(tp, - ser_status[unit].dcd ? 1 : 0); + /* + * the sun3 (and sparc) ports also have a 8530 (zs) + * and in that code, the authors winge about the + * zilog hardware flow control braindeath syndrome. + * [when dcd goes low with hw flow tx is disabled] + * Their solution is to turn hfc off when dcd goes + * off. We do similar, but instead, allow a software + * flow control that tx and rx interrupts try to + * figure out + */ +#ifdef use_hw_flow + if (ser_status[unit].state & SERCTL_DCD) { + tp->t_state |= TS_CARR_ON; + if (tp->t_cflag&CRTSCTS) + serctl( tp->t_dev, SERCTL_AUTO, DMBIS ); + } else { + tp->t_state &= ~TS_CARR_ON; + if (tp->t_cflag&CRTSCTS) + serctl( tp->t_dev, SERCTL_AUTO, DMBIC ); } +#endif use_hw_flow + +#ifdef DEBUG + ser_dbg("carrier",unit); +#endif + (*linesw[tp->t_line].l_modem)(tp, + (ser_status[unit].state & SERCTL_DCD) ? 1 : 0); + } - /* check for change in CTS */ - if (ser_status[unit].dcts) { - s = splhigh(); - ser_status[unit].dcts = 0; - splx(s); - if ((tp->t_state & TS_ISOPEN) && - (tp->t_flags & CRTSCTS)) { - tp->t_state &= ~TS_TTSTOP; - serstart(tp); - } else - tp->t_state |= TS_TTSTOP; - } - /* drain input fifo */ + + /* + * drain input fifo + */ while (ser_inlen[unit] > 0) { if (tp->t_rawq.c_cc + tp->t_canq.c_cc >= TTYHOG) { setsoftserial(); + ser_status[unit].intpend++; break; } s = splhigh(); @@ -532,15 +1054,21 @@ sersir(void) ser_intail[unit] = (ser_intail[unit] + 1) % INBUFLEN; ser_inlen[unit]--; splx(s); +#ifdef DEBUG + if ( c&(TTY_FE|TTY_PE) || !c) + printf("ser%d: l_rint( %x, )\n", unit, c); +#endif if (tp->t_state & TS_ISOPEN) (*linesw[tp->t_line].l_rint)(c, tp); } - /* fill output fifo */ + /* + * fill output fifo + */ if (ser_outlen[unit] == 0) { if (tp->t_line) (*linesw[tp->t_line].l_start)(tp); else - serstart(tp); + serstart(tp); } } } @@ -552,10 +1080,10 @@ serioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) register int unit = UNIT(dev); register int error; -#if defined(DEBUG) - printf("ser: entering ioctl()\n"); -#endif + if (unit>NSER) return ENOTTY; + tp = ser_tty[unit]; + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return (error); @@ -564,111 +1092,190 @@ serioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) return (error); switch (cmd) { -#if 0 - case TIOCSBRK: /* turn break on */ - dca->dca_cfcr |= CFCR_SBREAK; - break; - case TIOCCBRK: /* turn break off */ - dca->dca_cfcr &= ~CFCR_SBREAK; - break; -#endif - case TIOCSDTR: /* set DTR */ - (void) serctl(dev, SER_W5_DTR | SER_W5_RTS, DMBIS); - break; - - case TIOCCDTR: /* clear DTR */ - (void) serctl(dev, SER_W5_DTR | SER_W5_RTS, DMBIC); - break; - - case TIOCMSET: /* set modem control bits */ - (void) serctl(dev, *(int *)data, DMSET); - break; - - case TIOCMBIS: /* OR bits on */ - (void) serctl(dev, *(int *)data, DMBIS); - break; - - case TIOCMBIC: /* AND bits off */ - (void) serctl(dev, *(int *)data, DMBIC); - break; - - case TIOCMGET: /* get modem bits */ - *(int *)data = serctl(dev, 0, DMGET); - break; - - case TIOCGFLAGS: - { - int bits = 0; - - *(int *)data = bits; + case TIOCSBRK: /* turn break on */ + (void) serctl(dev, SERCTL_BRK, DMBIS); break; - } + + case TIOCCBRK: /* turn break off */ + (void) serctl(dev, SERCTL_BRK, DMBIC); + break; + + case TIOCSDTR: /* set DTR */ + (void) serctl(dev, SERCTL_DTR | SERCTL_RTS, DMBIS); + break; + + case TIOCCDTR: /* clear DTR */ + (void) serctl(dev, SERCTL_DTR | SERCTL_RTS, DMBIC); + break; + + case TIOCGFLAGS: + { + int bits = 0; + + if (!tp) return(ENOTTY); + + /* + * the (possibly incorrect) strategy here is + * to keep the tty structure up to date + * with TIOC flags + */ + + if (tp->t_cflag&CLOCAL) { + bits|=TIOCFLAG_CLOCAL; + bits|=TIOCFLAG_SOFTCAR; + } + + if (tp->t_cflag&CRTSCTS) + bits|=TIOCFLAG_CRTSCTS; + + if (tp->t_cflag&MDMBUF) + bits|=TIOCFLAG_MDMBUF; + + + *(int *)data = bits; + break; + } case TIOCSFLAGS: - { - int userbits, driverbits = 0; + { + int userbits, driverbits = 0; - error = suser(p->p_ucred, &p->p_acflag); - if (error != 0) - return (EPERM); - userbits = *(int *)data; + if (!tp) return(ENOTTY); + error = suser(p->p_ucred, &p->p_acflag); + if (error != 0) + return (EPERM); + + userbits = *(int *)data; + + if (userbits&(TIOCFLAG_CLOCAL|TIOCFLAG_SOFTCAR)) + tp->t_cflag |= CLOCAL; + else + tp->t_cflag &=~CLOCAL; + + if (userbits&TIOCFLAG_CRTSCTS) + tp->t_cflag |= CRTSCTS; + else + tp->t_cflag &=~CRTSCTS; + + if (userbits&TIOCFLAG_MDMBUF) + tp->t_cflag |= MDMBUF; + else + tp->t_cflag &=~MDMBUF; + + break; + } + +#ifdef allow_fiddle + case TIOCMSET: /* set modem control bits */ + (void) serctl(dev, *(int *)data, DMSET); break; - } - default: -#if defined(DEBUG) - printf("ser%d: unknown ioctl(,0x%x,)\n", UNIT(dev), cmd); -#endif - return (ENOTTY); + case TIOCMBIS: /* OR bits on */ + (void) serctl(dev, *(int *)data, DMBIS); + break; + + case TIOCMBIC: /* AND bits off */ + (void) serctl(dev, *(int *)data, DMBIC); + break; + + case TIOCMGET: /* get modem bits */ + *(int *)data = serctl(dev, 0, DMGET); + break; +#endif allow_fiddle + + default: + return (ENOTTY); } -#if defined(DEBUG) - printf("ser: exiting ioctl()\n"); -#endif return (0); } static int -ser_calc_regs(int unit, int cflag, unsigned char *preg3, unsigned char *preg4, - unsigned char *preg5) +ser_apply_state( int unit ) { + int s, state = ser_status[unit].state; + struct tty*tp = ser_tty[unit]; unsigned char r3, r4, r5; - r3 = SER_W3_ENBRX; - r5 = SER_W5_ENBTX; - if (ser_status[unit].dtr) - r5 |= SER_W5_DTR; - if (ser_status[unit].rts) - r5 |= SER_W5_RTS; - switch (cflag&CSIZE) { - case CS5: - r3 |= SER_W3_RX5DBITS; - r5 |= SER_W5_TX5DBITS; - break; - case CS6: - r3 |= SER_W3_RX6DBITS; - r5 |= SER_W5_TX6DBITS; - break; - case CS7: - r3 |= SER_W3_RX7DBITS; - r5 |= SER_W5_TX7DBITS; - break; - case CS8: + /* + * Here, we read the status bits from ser_status.state + * and write to write registers 3 4 and 5. + * In addition, we look at the tty struct (if any) + * and set up some stuff using that too. Basically, + * we bring the scc into line with what the flags are + */ + + r3 = r4 = r5 = 0; + + r4 |= SER_W4_CLKX16; /* XXX not the case with appletalk */ + + if (state&SERCTL_DTR) r5 |= SER_W5_DTR; + if (state&SERCTL_RTS) r5 |= SER_W5_RTS; + if (state&SERCTL_BRK) r5 |= SER_W5_BREAK; + + if (tp) { + + if (tp->t_cflag&CREAD) + r3 |= SER_W3_ENBRX; + + switch (tp->t_cflag&CSIZE) { + case CS5: + r3 |= SER_W3_RX5DBITS; + r5 |= SER_W5_TX5DBITS; + break; + case CS6: + r3 |= SER_W3_RX6DBITS; + r5 |= SER_W5_TX6DBITS; + break; + case CS7: + r3 |= SER_W3_RX7DBITS; + r5 |= SER_W5_TX7DBITS; + break; + case CS8: + r3 |= SER_W3_RX8DBITS; + r5 |= SER_W5_TX8DBITS; + break; + } + + if (tp->t_cflag & PARENB) + r4 |= (tp->t_cflag & PARODD)?SER_W4_PARODD:SER_W4_PAREVEN; + if (tp->t_cflag & CSTOPB) + r4 |= SER_W4_2SBIT; + else + r4 |= SER_W4_1SBIT; +#ifdef use_hw_flow + if (tp->t_cflag & CRTSCTS && state&SERCTL_DCD) + state|=SERCTL_AUTO; +#endif + if (tp->t_cflag & CRTSCTS && !state&SERCTL_CTS) + state|=SERCTL_INHIB; + } else { + /* we don't have a tty struct */ + r4 |= SER_W4_1SBIT; /* arbitary default of 8N1 */ r3 |= SER_W3_RX8DBITS; r5 |= SER_W5_TX8DBITS; - break; } - r4 = 0; - if(cflag & PARENB) - r4 |= (cflag & PARODD) ? SER_W4_PARODD : SER_W4_PAREVEN; - if(cflag & CSTOPB) - r4 |= SER_W4_2SBIT; - else - r4 |= SER_W4_1SBIT; - - *preg3 = r3; - *preg4 = r4; - *preg5 = r5; + + s = splhigh(); + + /* next two are fairly volatile */ + + if (!(state&SERCTL_INHIB)) + r5 |= SER_W5_ENBTX; +#ifdef use_hw_flow + if (state&SERCTL_AUTO) + r3 |= SER_W3_AUTOEN; +#endif + +#ifdef DEBUG + if (DBG(unit)) + printf("ser%d: apply w3<-%02x w4<-%02x w5<-%02x\n",unit,r3,r4,r5); + ser_dbg("apply",unit); +#endif + SER_WRITEREG(unit, 3, r3); + SER_WRITEREG(unit, 4, r4); + SER_WRITEREG(unit, 5, r5); + splx(s); } static int @@ -680,44 +1287,32 @@ serparam(register struct tty *tp, register struct termios *t) int ospeed = t->c_ospeed; int s; -#if defined(DEBUG) - printf("ser: entering serparam()\n"); -#endif /* check requested parameters */ - if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed)){ + if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed)){ printf("ser: serparam() returning EINVAL\n"); - return (EINVAL); + return (EINVAL); } - /* and copy to tty */ - tp->t_ispeed = t->c_ispeed; - tp->t_ospeed = t->c_ospeed; - tp->t_cflag = cflag; + /* and copy to tty */ + tp->t_ispeed = t->c_ispeed; + tp->t_ospeed = t->c_ospeed; + tp->t_cflag = cflag; /* Start of serial specific param code */ - if(ospeed == 0) { - serctl(unit, 0, DMSET); /* hang up line */ + if (ospeed == 0) { + serctl(unit, SERCTL_DTR , DMBIC); /* hang up line */ return(0); } serctl(unit, ospeed, SCC_SPEED); -/* - ser_calc_regs(unit, cflag, ®3, ®4, ®5); - s = splhigh(); - SER_DOCNTL(unit, 3, reg3); - SER_DOCNTL(unit, 4, reg4); - SER_DOCNTL(unit, 5, reg5); - splx(s); -*/ - serctl(unit, 1, SCC_INT); - serctl(unit, SER_W5_DTR | SER_W5_RTS, DMSET); + ser_apply_state( unit ); + + /* serctl(unit, 1, SCC_INT); */ /* why? it should already be on */ + serctl(unit, SERCTL_DTR | SERCTL_RTS, DMSET); /* End of serial specific param code */ -#if defined(DEBUG) - printf("ser: exiting serparam()\n"); -#endif return (0); } @@ -730,7 +1325,7 @@ serstart(register struct tty *tp) unit = UNIT(tp->t_dev); s = spltty(); if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP)) { - goto out; + goto out; } if (tp->t_outq.c_cc <= tp->t_lowat) { if (tp->t_state&TS_ASLEEP) { @@ -740,11 +1335,12 @@ serstart(register struct tty *tp) selwakeup(&(tp->t_wsel)); } if (tp->t_outq.c_cc == 0 || (tp->t_state & TS_BUSY) || - (ser_status[unit].flags & SER_BUSY)) - goto out; + (ser_status[unit].state & SERCTL_BUSY)) { + goto out; + } tp->t_state |= TS_BUSY; - if(ser_outlen[unit] == 0){ + if (ser_outlen[unit] == 0){ first_char = (char)getc(&tp->t_outq); need_start = 1; } else @@ -758,7 +1354,7 @@ serstart(register struct tty *tp) space = OUTBUFLEN - ser_outlen[unit]; splx(s1); - while(tp->t_outq.c_cc && space > 0) { + while (tp->t_outq.c_cc && space > 0) { /* note that getc goes spltty() */ c = getc(&tp->t_outq); /* protect s/w fifo at splhigh() */ @@ -768,12 +1364,12 @@ serstart(register struct tty *tp) splx(s1); space--; } - tp->t_state &= ~TS_BUSY; + tp->t_state &= ~TS_BUSY; if (need_start) { s1 = splhigh(); - ser_status[unit].flags |= SER_BUSY; - SCCRDWR(unit) = first_char; /* to start chain */ + ser_status[unit].state |= SERCTL_BUSY; + SCCRDWR(unit) = first_char; /* to start chain */ splx(s1); } @@ -790,17 +1386,15 @@ serstop(register struct tty *tp, int flag) { register int s; -#if defined(DEBUG) - printf("ser: entering serstop()\n"); +#ifdef DEBUG + ser_dbg("serstop",UNIT(tp->t_dev)); #endif + s = spltty(); if (tp->t_state & TS_BUSY) { if ((tp->t_state&TS_TTSTOP)==0) tp->t_state |= TS_FLUSH; } -#if defined(DEBUG) - printf("ser: exiting serstop()\n"); -#endif splx(s); } @@ -813,39 +1407,73 @@ serctl(dev_t dev, int bits, int how) /* run at splhigh so we don't get interrupted by i/o */ s = splhigh(); switch (how) { + case DMSET: + ser_status[unit].state = + (ser_status[unit].state & ~SERCTL_MASK)| + (bits&SERCTL_MASK); + ser_apply_state(unit); + break; - case DMSET: - ser_status[unit].dtr = bits & SER_W5_DTR; - ser_status[unit].rts = bits & SER_W5_RTS; - SER_DOCNTL(unit, 5, bits | 0x68); - break; + case DMBIS: + ser_status[unit].state = + (ser_status[unit].state)|(bits&SERCTL_MASK); + ser_apply_state(unit); + break; - case DMBIS: - break; + case DMBIC: + ser_status[unit].state = + (ser_status[unit].state)&~(bits&SERCTL_MASK); + ser_apply_state(unit); + break; - case DMBIC: - break; + case DMGET: + bits = ser_status[unit].state; + break; - case DMGET: - bits = SER_STATUS(unit, 0); - break; + case SCC_INT: +#ifdef DEBUG + if (DBG(unit)) + printf("ser%d: %s interrupts\n",unit, + bits?"enabling":"disabling"); +#endif + if (bits) { + /* + * Why not a channel reset? + * This would mean sending the initialisation string + * with a bit of a (tiny) delay after the chan reset + * In addition, cts/dcd might change which an + * external device plugged in might object to. + * + * soft-resetting all the flaggy bits should + * do the trick + */ + SER_WRITEREG(unit, 0, SER_W0_RSTESINTS| + SER_W0_RSTTXUNDERRUN); + SER_WRITEREG(unit, 0, SER_W0_RSTERR); + SER_WRITEREG(unit, 0, SER_W0_RSTIUS); + SER_WRITEREG(unit, 1, + SER_W1_ENBEXTINT | + /*either*/ SER_W1_ENBRXINT | + /*or*/ /*SER_W1_ENBR1INT |*/ + SER_W1_ENBTXINT | + 0); + SER_WRITEREG(unit, 15, + SER_W15_DCDINT| + SER_W15_CTSINT| + SER_W15_BRKINT| + 0); + ser_apply_state(unit); /* turn all the other stuff on */ + } else { + SER_WRITEREG(unit, 1, 0); /* disables all interrupts */ + SER_WRITEREG(unit, 15, 0); + } + break; + + case SCC_SPEED: + SER_WRITEREG(unit, 12, SERBRD(bits) & 0xff); + SER_WRITEREG(unit, 13, (SERBRD(bits) >> 8) & 0xff); + break; - /* */ - case SCC_INT: - if (bits) { - SER_DOCNTL(unit, 0, SER_W0_RSTERR); - SER_DOCNTL(unit, 0, SER_W0_RSTIUS); - SER_DOCNTL(unit, 1, - SER_W1_ENBEXTINT | - SER_W1_ENBRXINT | - SER_W1_ENBTXINT); - } else - SER_DOCNTL(unit, 1, 0); - break; - case SCC_SPEED: - SER_DOCNTL(unit, 12, SERBRD(bits) & 0xff); - SER_DOCNTL(unit, 13, (SERBRD(bits) >> 8) & 0xff); - break; } (void) splx(s); @@ -856,23 +1484,23 @@ serctl(dev_t dev, int bits, int how) * Console functions. */ -dev_t mac68k_serdev; +dev_t mac68k_serdev; sercnprobe(struct consdev *cp) { int maj, unit; for (maj = 0 ; maj < nchrdev ; maj++) { - if (cdevsw[maj].d_open == seropen) { - break; - } + if (cdevsw[maj].d_open == seropen) { + break; + } } if (maj == nchrdev) - goto nosercon; + goto nosercon; cp->cn_pri = CN_NORMAL; /* Lower than CN_INTERNAL */ if (mac68k_machine.serial_console & 0x01) - cp->cn_pri = CN_REMOTE; /* Higher than CN_INTERNAL */ + cp->cn_pri = CN_REMOTE; /* Higher than CN_INTERNAL */ unit = (mac68k_machine.serial_console & 0x02) ? 1 : 0; @@ -885,7 +1513,7 @@ nosercon: if (mac68k_machine.serial_boot_echo) { /* major number doesn't really matter. */ mac68k_serdev = makedev(maj, 0); - serinit(1); + serinit(1); } return 0; @@ -893,28 +1521,29 @@ nosercon: sercninit(struct consdev *cp) { - serinit(1); + serinit(1); } sercngetc(dev_t dev) { - int unit, c; + int unit, c; - unit = UNIT(dev); + unit = UNIT(dev); - while (!(SER_STATUS(unit, 0) & SER_R0_RXREADY)); - c = SCCRDWR(unit); - SER_STATUS(unit, 0) = SER_W0_RSTESINTS; + while (!(SER_STATUS(unit, 0) & SER_R0_RXREADY)); + c = SCCRDWR(unit); + SER_STATUS(unit, 0) = SER_W0_RSTESINTS; - return c; + return c; } sercnputc(dev_t dev, int c) { - int unit; + int unit; - unit = UNIT(dev); + unit = UNIT(dev); - while (!(SER_STATUS(unit, 0) & SER_R0_TXREADY)); - SCCRDWR(unit) = c; +gray_bar(); + while (!(SER_STATUS(unit, 0) & SER_R0_TXREADY)); + SCCRDWR(unit) = c; } diff --git a/sys/arch/mac68k/dev/serreg.h b/sys/arch/mac68k/dev/serreg.h index f5ce270351c5..1d5054aa78ce 100644 --- a/sys/arch/mac68k/dev/serreg.h +++ b/sys/arch/mac68k/dev/serreg.h @@ -1,9 +1,9 @@ -/* $NetBSD: serreg.h,v 1.5 1994/10/26 08:46:21 cgd Exp $ */ +/* $NetBSD: serreg.h,v 1.6 1995/02/11 19:06:59 briggs Exp $ */ /* - * Copyright (C) 1993 Allen K. Briggs, Chris P. Caputo, - * Michael L. Finch, Bradley A. Grantham, and - * Lawrence A. Kesteloot + * Copyright (C) 1993 Allen K. Briggs, Chris P. Caputo, + * Michael L. Finch, Bradley A. Grantham, and + * Lawrence A. Kesteloot * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -16,7 +16,7 @@ * 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 Alice Group. + * This product includes software developed by the Alice Group. * 4. The names of the Alice Group or any of its members may not be used * to endorse or promote products derived from this software without * specific prior written permission. @@ -32,77 +32,220 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * Mac II serial device interface * - * Information used in this source was gleaned from low-memory - * global variables in MacOS and the Advanced Micro Devices - * 1992 Data Book/Handbook. + * Mac II serial device interface + * + * Information used in this source was gleaned from low-memory + * global variables in MacOS and the Advanced Micro Devices + * 1992 Data Book/Handbook. */ /* Gleaned from MacOS */ -extern volatile unsigned char *sccA; +extern volatile unsigned char *sccA; -#define SER_W0_RSTESINTS 0x10 /* Reset ext/status interrupts */ -#define SER_W0_ENBRXRDY 0x20 /* Enable interrupt on next receive */ -#define SER_W0_RSTTXPND 0x28 /* Reset transmit interrupt pending */ -#define SER_W0_RSTERR 0x30 /* Reset error */ -#define SER_W0_RSTIUS 0x38 /* Reset highest interrupt pending */ -#define SER_W0_RSTTXUNDERRUN 0xc0 /* Reset transmit underrun/EOM latch */ +/* + * Following information taken from Zilog's SCC User manal(1992) and the + * Zilog up and Peripherals databook (vol1/1992) + * + * Interrupt Source Priority: + * channel A rx -highest + * channel A tx + * channel A ext/status + * channel B rx + * channel B tx + * channel B ext/status -lowest + * + */ -#define SER_W1_ENBEXTINT 0x01 /* Enable external int */ -#define SER_W1_ENBTXINT 0x02 /* Enable transmit ready interrupt */ -#define SER_W1_ENBR1INT 0x08 /* Rx Int on first char/special cond */ -#define SER_W1_ENBRXINT 0x10 /* Rx Int on all chars/special cond */ +/* + * Write register 0 (Command Register) + */ + +#define SER_W0_RSTESINTS 0x10 /* Reset ext/status interrupts */ + /* after an ext/status interrupt the + * status bits are latched into RR0 + * This re-enables the bits and allows + * further interrupts due to ext/status + * change + */ +#define SER_W0_ENBRXRDY 0x20 /* Enable interrupt on next receive */ + /* if using interrupt on 1st rx'd char + * then this is used to reactivate + * ints on rx after youve read the rx + * fifo, otherwise you get another int + * straight away if there's another + * rx char in the fifo + */ +#define SER_W0_RSTTXPND 0x28 /* Reset transmit interrupt pending */ + /* used when there are no more chars + * to be sent. used to stop the tx'er + * from int'ing when the tx buffer + * becomes empty (with tx ints enabl'd) + */ +#define SER_W0_RSTERR 0x30 /* Reset error */ + /* resets error bits in RR1. the datum + * assoc'd with the error is held in + * rx fifo and is lost after this cmd + */ +#define SER_W0_RSTIUS 0x38 /* Reset highest interrupt pending */ + /* used as the last cmd in an interrupt + * service: lets lower priority + * interrupts to request service + */ +#define SER_W0_RSTTXUNDERRUN 0xc0 /* Reset transmit underrun/EOM latch */ + /* when TX underrun/eom latch has been + * reset, the scc sends an abort and + * flag on underrun. this command resets + * that latch. + */ +/* + * Write register 1 (tx/rx interrupt and data transfer mode definition) + */ -#define SER_W3_ENBRX 0x01 /* Enable reception */ -#define SER_W3_RX5DBITS 0x00 /* Receive 5 data bits */ -#define SER_W3_RX6DBITS 0x80 /* Receive 6 data bits */ -#define SER_W3_RX7DBITS 0x40 /* Receive 7 data bits */ -#define SER_W3_RX8DBITS 0xC0 /* Receive 8 data bits */ +#define SER_W1_ENBEXTINT 0x01 /* Enable external int */ +#define SER_W1_ENBTXINT 0x02 /* Enable transmit ready interrupt */ +#define SER_W1_PARISSPEC 0x04 /* parity err is a special cond */ +#define SER_W1_ENBR1INT 0x08 /* Rx Int on first char/special cond */ +#define SER_W1_ENBRXINT 0x10 /* Rx Int on all chars/special cond */ -#define SER_W4_PARNONE 0x00 /* No parity */ -#define SER_W4_PARODD 0x01 /* Odd parity */ -#define SER_W4_PAREVEN 0x03 /* Even parity */ -#define SER_W4_1SBIT 0x04 /* 1 stop bit */ -#define SER_W4_2SBIT 0x0c /* 2 stop bits */ +/* + * Write register 2 (interrupt vector) - see WR9 + */ -#define SER_W5_RTS 0x02 /* RTS enable */ -#define SER_W5_ENBTX 0x08 /* Enable transmission */ -#define SER_W5_BREAK 0x10 /* Send break */ -#define SER_W5_TX5DBITS 0x00 /* Send 5 data bits */ -#define SER_W5_TX6DBITS 0x40 /* Send 6 data bits */ -#define SER_W5_TX7DBITS 0x20 /* Send 7 data bits */ -#define SER_W5_TX8DBITS 0x60 /* Send 8 data bits */ -#define SER_W5_DTR 0x80 /* DTR enable */ +/* + * Write register 3 (rx parameters and Control) + * can read from RR9 with extended read option on (for eSCC) + */ -#define SER_W9_HWRESET 0xC0 /* Force Hardware Reset */ -#define SER_W9_NV 0x02 /* There is no interrupt vector */ -#define SER_W9_DLC 0x04 /* Disable lower interrupt chain */ -#define SER_W9_MIE 0x08 /* Enable master interrupt */ +#define SER_W3_ENBRX 0x01 /* rx enable */ +#define SER_W3_AUTOEN 0x20 /* auto enable */ + /* causes CTS to become the tx + * enable and DCD to become the rx + * enable. + */ +#define SER_W3_RX5DBITS 0x00 /* Receive 5 data bits */ +#define SER_W3_RX6DBITS 0x80 /* Receive 6 data bits */ +#define SER_W3_RX7DBITS 0x40 /* Receive 7 data bits */ +#define SER_W3_RX8DBITS 0xC0 /* Receive 8 data bits */ -#define SER_W10_NRZ 0x00 /* Set NRZ encoding */ -#define SER_W11_TXBR 0x80 /* Transmit clock is BR generator */ -#define SER_W11_RXBR 0x40 /* Receive clock is BR generator */ -#define SER_W14_ENBBR 0x01 /* Enable BR generator */ -#define SER_W15_ABRTINT 0x80 /* Abort pending interrups */ +/* + * Write Register 4 (tx/rx misc param and modes) + */ -#define SER_R0_RXREADY 0x01 /* Received character available */ -#define SER_R0_TXREADY 0x04 /* Ready to transmit character */ -#define SER_R0_DCD 0x08 /* Carrier detect */ -#define SER_R0_CTS 0x20 /* Clear to send */ -#define SER_R0_TXUNDERRUN 0x40 /* Tx Underrun/EOM */ +#define SER_W4_PARNONE 0x00 /* No parity */ +#define SER_W4_PARODD 0x01 /* Odd parity */ +#define SER_W4_PAREVEN 0x03 /* Even parity */ +#define SER_W4_1SBIT 0x04 /* 1 stop bit */ +#define SER_W4_2SBIT 0x0c /* 2 stop bits */ +#define SER_W4_CLKX1 0x00 /* clock rate = data rate */ +#define SER_W4_CLKX16 0x40 /* clock rate = 16 * data rate */ +#define SER_W4_CLKX32 0x80 /* clock rate = 32 * data rate */ +#define SER_W4_CLKX64 0xc0 /* clock rate = 64 * data rate */ -#define SER_R1_RXOVERRUN 0x20 -#define SER_R1_PARITYERR 0x10 -#define SER_R1_CRCERR 0x40 -#define SER_R1_ENDOFFRAME 0x80 +/* + * Write Register 5 (Tx params and control) + */ -#define SERBRD(x) (mac68k_machine.sccClkConst / (x) - 2) -#define SCCCNTL(unit) (sccA[2 - ((unit) << 1)]) -#define SCCRDWR(unit) (sccA[6 - ((unit) << 1)]) +#define SER_W5_RTS 0x02 /* RTS enable */ +#define SER_W5_ENBTX 0x08 /* Enable transmission */ +#define SER_W5_BREAK 0x10 /* Send break */ +#define SER_W5_TX5DBITS 0x00 /* Send 5 data bits */ +#define SER_W5_TX6DBITS 0x40 /* Send 6 data bits */ +#define SER_W5_TX7DBITS 0x20 /* Send 7 data bits */ +#define SER_W5_TX8DBITS 0x60 /* Send 8 data bits */ +#define SER_W5_DTR 0x80 /* DTR enable */ -#define SER_DOCNTL(unit, reg, val) \ - {SCCCNTL(unit) = (reg); SCCCNTL(unit) = (val);} -#define SER_STATUS(unit, reg) \ - (SCCCNTL(unit) = (reg), SCCCNTL(unit)) +/* + * Write register 8 (Transmit buffer) + */ + +/* + * Write Register 9 (Master interrupt control) + */ +#define SER_W9_ARESET 0x80 /* reset channel A */ +#define SER_W9_BRESET 0x40 /* reset channel A */ +#define SER_W9_HWRESET ( SER_W9_ARESET | SER_W9_BRESET ) /* both */ +#define SER_W9_NV 0x02 /* There is no interrupt vector */ +#define SER_W9_DLC 0x04 /* Disable lower interrupt chain */ +#define SER_W9_MIE 0x08 /* Enable master interrupt */ + /* (MIE is cleared on a HWRESET) */ + +/* Write Register 10 (Misc tx/rx control bits */ + +#define SER_W10_NRZ 0x00 /* Set NRZ encoding */ +#define SER_W10_URFLG 0x04 /* abort/flag on underrun (sdlc only) */ + +/* Write Register 11 (Clock mode control) */ + +#define SER_W11_TXBR 0x10 /* Transmit clock is BR generator */ +#define SER_W11_RXBR 0x40 /* Receive clock is BR generator */ + +/* Write Register 12 (Lower byte of baud constant) + * Write Register 13 (Upper byte of baud constant) + * Write Register 14 (Misc control of baud) + * + * The baud constant is computed by: + * + * Baud constant = ( clock_freq / ( 2* desired_rate * BR_clk_period )) - 2 + */ + +#define SERBRD(x) (mac68k_machine.sccClkConst / (x) -2 ) + +#define SER_W14_ENBBR 0x01 /* Enable BR generator */ + +/* + * Write Register 15 (Ext/ststus interrupt control) + */ + +#define SER_W15_DCDINT 0x08 /* enable DCD interrupts */ +#define SER_W15_CTSINT 0x20 /* enable CTS interrupts */ +#define SER_W15_BRKINT 0x80 /* Abort pending interrups */ + +/* + * Read Register 0 (tx/rx buffer status & ext status) + */ + +#define SER_R0_RXREADY 0x01 /* Received character available */ +#define SER_R0_TXREADY 0x04 /* Ready to transmit character */ +#define SER_R0_DCD 0x08 /* Carrier detect */ +#define SER_R0_CTS 0x20 /* Clear to send */ +#define SER_R0_TXUNDERRUN 0x40 /* Tx Underrun/EOM */ +#define SER_R0_BREAK 0x80 /* Break/abort */ + +#define SER_R1_RXOVERRUN 0x20 +#define SER_R1_PARITYERR 0x10 +#define SER_R1_CRCERR 0x40 +#define SER_R1_ENDOFFRAME 0x80 + +/* + * Read Register 2 + * channel A: is contents of WR2 + * channel B: is status of an interrupt (low here if WR9 appropriate) + */ + +#define SER_R2_MASK 0x06 /* what kind of int was it */ +#define SER_R2_TX 0x00 /* tx buffer empty */ +#define SER_R2_EXTCHG 0x02 /* external/status change */ +#define SER_R2_RX 0x04 /* rx char avail */ +#define SER_R2_SPEC 0x06 /* special recv condition */ + +#define SER_R2_CHANMASK 0x08 /* which channell caused int */ +#define SER_R2_CHANA 0x08 +#define SER_R2_CHANB 0x00 + +/* only from channel A */ +#define SER_R3_BIPES 0x01 /* chanB ext/stat interrupt pending */ +#define SER_R3_BIPTX 0x02 /* chanB tx ip */ +#define SER_R3_BIPRX 0x04 /* chanB rx ip */ +#define SER_R3_AIPES 0x08 /* chanA ext/stat interrupt pending */ +#define SER_R3_AIPTX 0x10 /* chanA tx ip */ +#define SER_R3_AIPRX 0x20 /* chanA rx ip */ + +#define SCCCNTL(unit) (sccA[2 - ((unit) << 1)]) +#define SCCRDWR(unit) (sccA[6 - ((unit) << 1)]) + +#define SER_DOCNTL(unit, reg, val) \ + SCCCNTL(unit) = (reg), SCCCNTL(unit) = (val) +#define SER_STATUS(unit, reg) \ + (SCCCNTL(unit) = (reg), SCCCNTL(unit))