/* * Copyright (c) 1992 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Sony Corp. and Kazumasa Utashiro of Software Research Associates, Inc. * * %sccs.include.redist.c% * * from: $Hdr: scc.c,v 4.300 91/06/09 06:44:53 root Rel41 $ SONY * * @(#)scc.c 7.4 (Berkeley) 03/10/93 */ /* * LH8530 SCC (serial communication controller) driver * * NOTE: This driver is available only for news700/1200/1700/3400. * * Any code and mechanism in this module may not be used * in any form without permissions. COPYRIGHT (C) 1989- * SONY Corporation, Super Microsystems Group (SMSG), * Work Station Division, all rights RESERVED. */ #include #include "rs.h" #include #include #include #include #include #include #include #include #define PROBE_DATA 0x55 #ifdef mips #define VOLATILE volatile #else #define VOLATILE #endif int tty00_is_console = 0; #define SCC_BUFSIZE 256 char scc_buf[2][SCC_BUFSIZE]; scc_open(chan) int chan; { register Scc_channel *scc = &sccsw[chan]; register int s; s = splscc(); if ((scc->scc_status & CHAN_ACTIVE) == 0) { scc_init(chan); if (chan <= SCC_REMOTE1) scc->r_dma.dma_addr = scc_buf[chan]; else scc->r_dma.dma_addr = malloc(SCC_BUFSIZE, M_DEVBUF, M_WAITOK); scc->r_dma.dma_count = 0; scc->scc_status |= CHAN_ACTIVE; } (void) splx(s); return (0); } scc_probe(chan) register int chan; { VOLATILE struct scc_reg *port = sccsw[chan].scc_port; int s, temp, probe; if (badaddr(port, 1)) return (0); s = splscc(); temp = scc_read_reg(chan, RR12); scc_write_reg(chan, WR12, PROBE_DATA); probe = scc_read_reg(chan, RR12); scc_write_reg(chan, WR12, temp); (void) splx(s); return (probe == PROBE_DATA); } scc_getc(chan) int chan; { VOLATILE struct scc_reg *port = sccsw[chan].scc_port; int c; if (port->ctrl & R0_RxCA) { SCCWAIT; c = port->data; SCCWAIT; return (c); } SCCWAIT; return (-1); } #ifndef notdef scc_putc(chan, c) int chan, c; { register VOLATILE struct scc_reg *port = sccsw[chan].scc_port; while ((port->ctrl & R0_TxBE) == 0) SCCWAIT; SCCWAIT; port->data = c; SCCWAIT; } #else scc_putc(chan, c) int chan, c; { register VOLATILE struct scc_reg *port = sccsw[chan].scc_port; register VOLATILE u_char *ctrl = &sccsw[chan].scc_port->ctrl; register VOLATILE u_char *data = &sccsw[chan].scc_port->data; SCCWAIT; while ((*ctrl & R0_TxBE) == 0) { SCCWAIT; } SCCWAIT; *ctrl = W0_RES_TxINT; SCCWAIT; *data = (char)(c & 0xff); SCCWAIT; scc_write_reg(chan, WR1, W1_RxINT_ALL|W1_TxINTE|W1_EXTINTE|W1_PARITY); SCCWAIT; } #endif scc_init(chan) int chan; { register VOLATILE struct scc_reg *port; register char *data; register int i; register Scc_channel *scc = &sccsw[chan]; int s; s = splscc(); data = scc->scc_init; port = scc->scc_port; for (i = 0; i < N_INITDATA; i++) { port->ctrl = *data++; SCCWAIT; } scc_write_reg(chan, WR2, scc->scc_vec & ~0x0f); /* KU:XXX This must be bug because scc->scc_param is not initialized yet. scc_set_param(chan, scc->scc_param); */ (void) splx(s); } #define vec_to_scc(vec) ((((vec) - SCCVEC0) >> 3) & 0x000f) #define vec_to_chan(vec) scc2chan[vec_to_scc(vec)] int scc2chan[] = { 1, 0, 3, 2, 5, 4, 7, 6, 9, 8 }; scc_rint(vec) int vec; { int chan = vec_to_chan(vec); register Scc_channel *scc = &sccsw[chan]; register VOLATILE struct scc_reg *port = scc->scc_port; register int c; if ((scc->scc_status & CHAN_ACTIVE) == 0) { scc_reset(chan); goto out; } if (scc->scc_status & LINE_BREAK){ scc->scc_status &= ~LINE_BREAK; c = port->data; SCCWAIT; } while (port->ctrl & R0_RxCA) { SCCWAIT; c = port->data; SCCWAIT; #if NRS > 0 scc_pdma(chan, c); #endif } SCCWAIT; out: port->ctrl = W0_RES_IUS; SCCWAIT; } #if NRS > 0 scc_enable(chan) int chan; { register Scc_channel *scc = &sccsw[chan]; int n; int s; s = splscc(); if ((n = scc->r_dma.dma_count) > 0) { scc->r_dma.dma_count = 0; rsrint(chan, scc->r_dma.dma_addr, n); } else scc->scc_status |= ENABLE; (void) splx(s); } scc_pdma(chan, c) int chan; int c; { register Scc_channel *scc = &sccsw[chan]; int n; if (scc->r_dma.dma_count >= SCC_BUFSIZE) printf("rs%d soft fifo overflow\n", chan); else scc->r_dma.dma_addr[scc->r_dma.dma_count++] = c; if (scc->scc_status & ENABLE || scc->r_dma.dma_count >= SCC_BUFSIZE) { scc->scc_status &= ~ENABLE; n = scc->r_dma.dma_count; scc->r_dma.dma_count = 0; rsrint(chan, scc->r_dma.dma_addr, n); } } #endif /* NRS > 0 */ scc_xint(vec) int vec; { int chan = vec_to_chan(vec); register Scc_channel *scc = &sccsw[chan]; register VOLATILE struct scc_reg *port = scc->scc_port; if (scc->scc_status & OSTOP) scc->scc_status &= ~(OACTIVE|OSTOP); if (scc->scc_status & OFLUSH) { scc->x_dma.dma_count = 0; scc->scc_status &= ~(OACTIVE|OFLUSH); } if ((scc->scc_status & OACTIVE) && (scc->x_dma.dma_count > 0)) { port->data = *(scc->x_dma.dma_addr)++; SCCWAIT; scc->x_dma.dma_count--; } else { port->ctrl = W0_RES_TxINT; SCCWAIT; scc->scc_status &= ~OACTIVE; #if NRS > 0 if (scc->x_dma.dma_count == 0) rsxint(chan); #endif } port->ctrl = W0_RES_IUS; SCCWAIT; } scc_sint(vec) int vec; { int chan = vec_to_chan(vec); register Scc_channel *scc = &sccsw[chan]; register VOLATILE struct scc_reg *port = scc->scc_port; register int status; register int param = 0; port->ctrl = W0_RES_EXT; SCCWAIT; if ((scc->scc_status & CHAN_ACTIVE) == 0) { scc_reset(chan); goto out; } status = port->ctrl; SCCWAIT; if (status & R0_DCD) param |= DCD; if (status & R0_CTS) param |= CTS; if (status & R0_BREAK){ param |= RBREAK; scc->scc_status |= LINE_BREAK; } if ((scc->scc_param & (DCD|CTS|RBREAK)) != param) { scc->scc_param = (scc->scc_param & ~(DCD|CTS|RBREAK)) | param; #if NRS > 0 rssint(chan, scc->scc_param); #endif } out: port->ctrl = W0_RES_IUS; SCCWAIT; } scc_cint(vec) int vec; { int chan = vec_to_chan(vec); register Scc_channel *scc = &sccsw[chan]; register VOLATILE struct scc_reg *port = scc->scc_port; register int status; int c; if ((scc->scc_status & CHAN_ACTIVE) == 0) { scc_reset(chan); goto out; } status = scc_read_reg(chan, RR1); if (status & R1_CRC) scc->scc_param |= FRAMING_ERROR; if (status & R1_OVRUN) { if ((scc->scc_param & OVERRUN_ERROR) == 0) { scc->scc_param |= OVERRUN_ERROR; #if NRS > 0 rssint(chan, scc->scc_param); #endif } } if (status & R1_PARITY) { scc->scc_param |= SCC_PARITY_ERROR; while (port->ctrl & R0_RxCA) { SCCWAIT; c = port->data; SCCWAIT; #if NRS > 0 if (scc->scc_param & NOCHECK) scc_pdma(chan, c); #endif } SCCWAIT; } out: port->ctrl = W0_RES_ERROR; SCCWAIT; port->ctrl = W0_RES_IUS; SCCWAIT; } scc_write_reg(chan, reg, data) int chan, reg, data; { register VOLATILE struct scc_reg *port = sccsw[chan].scc_port; port->ctrl = reg; SCCWAIT; port->ctrl = data; SCCWAIT; } scc_read_reg(chan, reg) int chan, reg; { register VOLATILE struct scc_reg *port = sccsw[chan].scc_port; int result; port->ctrl = reg; SCCWAIT; result = port->ctrl; SCCWAIT; return (result); } #ifdef news3400 #define DSRA 0x01 #define RIA 0x02 #define DSRB 0x04 #define RIB 0x08 #define DSRC 0x01 #define RIC 0x02 #define DSRD 0x04 #define RID 0x08 #define DSRE 0x10 #define RIE 0x20 #define DSRF 0x40 #define RIF 0x80 #endif /* news3400 */ struct ri_dsr { char *status; int ri; int dsr; } ri_dsr[] = { { (char *)SCC_STATUS0, RIA, DSRA }, { (char *)SCC_STATUS0, RIB, DSRB }, #if !defined(news3200) { (char *)SCC_STATUS1, RIC, DSRC }, { (char *)SCC_STATUS1, RID, DSRD }, { (char *)SCC_STATUS1, RIE, DSRE }, { (char *)SCC_STATUS1, RIF, DSRF }, { (char *)SCC_STATUS2, RIC, DSRC }, { (char *)SCC_STATUS2, RID, DSRD }, { (char *)SCC_STATUS2, RIE, DSRE }, { (char *)SCC_STATUS2, RIF, DSRF } #endif /* !news3200 */ }; get_ri_dsr(chan) int chan; { register struct ri_dsr *p; register int status, param; param = 0; p = &ri_dsr[chan]; status = *p->status; if ((status & p->ri) == 0) param |= RI; if ((status & p->dsr) == 0) param |= DSR; return (param); } #if defined(news3400) /* * tc0 = floor(4915200 / 32 / baudrate - 2 + 0.5); */ static int tc0[] = { 0, /* B0 */ 3070, /* B50 */ 2046, /* B75 */ 1394, /* B110 */ 1144, /* B134 */ 1022, /* B150 */ 766, /* B200 */ 510, /* B300 */ 254, /* B600 */ 126, /* B1200 */ 83, /* B1800 */ 62, /* B2400 */ 30, /* B4800 */ 14, /* B9600 */ 6, /* EXTA (B19200) */ 2 /* EXTB (B38400) */ }; #endif /* news3400 */ static int tc1[] = { /* * tc1 = floor(3686400 / 32 / baudrate - 2 + 0.5); */ 0, /* B0 */ 2302, /* B50 */ 1534, /* B75 */ 1045, /* B110 */ 858, /* B134 */ 766, /* B150 */ 574, /* B200 */ 382, /* B300 */ 190, /* B600 */ 94, /* B1200 */ 62, /* B1800 */ 46, /* B2400 */ 22, /* B4800 */ 10, /* B9600 */ 4, /* B19200 */ 1, /* B38400 */ }; scc_set_param(chan, param) int chan; register int param; { register Scc_channel *scc = &sccsw[chan]; register int bit, baud, *tc; int s; s = splscc(); /* * Baud rate / external clock */ if ((baud = param & BAUD_RATE) == EXTB && chan <= SCC_REMOTE1 && param & EXTCLK_ENABLE) { scc_write_reg(chan, WR11, W11_RxC_RTxC|W11_TxC_TRxC); bit = W4_X1; } else { tc = (chan <= SCC_REMOTE1) ? tc0 : tc1; scc_write_reg(chan, WR11, W11_RxC_BRG|W11_TxC_BRG); scc_write_reg(chan, WR12, tc[baud] & 0xff); scc_write_reg(chan, WR13, tc[baud] >> 8); bit = W4_X16; } /* * Clock mode / parity / stop bit */ if (param & PARITY) { bit |= W4_PARITY; if (param & EVEN) bit |= W4_EVEN; } switch (param & STOPBIT) { case STOP1: bit |= W4_STOP1; break; case STOP1_5: bit |= W4_STOP1_5; break; case STOP2: bit |= W4_STOP2; break; } scc_write_reg(chan, WR4, bit); /* * Receiver enable / receive character size / auto enable */ bit = (param & RXE ? W3_RxE : 0); switch (param & CHAR_SIZE) { case C5BIT: break; case C6BIT: bit |= W3_Rx6BIT; break; case C7BIT: bit |= W3_Rx7BIT; break; case C8BIT: bit |= W3_Rx8BIT; break; } #ifdef AUTO_ENABLE if (param & AUTO_ENABLE) bit |= W3_AUTO; #endif /* AUTO_ENABLE */ scc_write_reg(chan, WR3, bit); /* * Transmitter enable / transmit character size / RTS / DTR / BREAK */ bit = (param & TXE ? W5_TxE : 0); switch (param & CHAR_SIZE) { case C5BIT: break; case C6BIT: bit |= W5_Tx6BIT; break; case C7BIT: bit |= W5_Tx7BIT; break; case C8BIT: bit |= W5_Tx8BIT; break; } if (param & RTS) bit |= W5_RTS; if (param & DTR) bit |= W5_DTR; if (param & XBREAK) bit |= W5_BREAK; scc_write_reg(chan, WR5, bit); scc->scc_param = param; (void) splx(s); return (0); } scc_get_param(chan) int chan; { register Scc_channel *scc = &sccsw[chan]; scc->scc_param = (scc->scc_param & ~(RI|DSR)) | get_ri_dsr(chan); return (scc->scc_param); } scc_get_status(chan) int chan; { return (sccsw[chan].scc_status); } scc_set_status(chan, stat) int chan, stat; { sccsw[chan].scc_status = stat; return (0); } scc_flush(chan) int chan; { register Scc_channel *scc = &sccsw[chan]; if (scc->scc_status & OACTIVE) scc->scc_status |= OFLUSH; else if (scc->x_dma.dma_count > 0) { scc->x_dma.dma_count = 0; #if NRS > 0 rsxint(chan); #endif } return (0); } scc_start(chan) int chan; { register Scc_channel *scc = &sccsw[chan]; if ((scc->scc_status & OACTIVE) == 0 && scc->x_dma.dma_count > 0) { scc->scc_port->data = *(scc->x_dma.dma_addr)++; SCCWAIT; scc->x_dma.dma_count--; scc->scc_status |= OACTIVE; } return (0); } scc_stop(chan) int chan; { register Scc_channel *scc = &sccsw[chan]; if (scc->scc_status & OACTIVE) scc->scc_status |= OSTOP; return (0); } scc_write(chan, buf, count) int chan; caddr_t buf; int count; { register Scc_channel *scc = &sccsw[chan]; if (count <= 0) return (0); scc->x_dma.dma_addr = buf; scc->x_dma.dma_count = count; scc_start(chan); return (count); } scc_error_write(chan, buf, count) int chan; register char *buf; register int count; { register int i; for (i = 0; i < count; i++) scc_putc(chan, *buf++); return (i); } scc_reset(chan) int chan; { register Scc_channel *scc = &sccsw[chan]; while (scc_getc(chan) != -1) ; scc->scc_status &= ~CHAN_ACTIVE; }