summaryrefslogtreecommitdiff
path: root/chips/scc_8530_hdw.c
diff options
context:
space:
mode:
authorThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
committerThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
commitf07a4c844da9f0ecae5bbee1ab94be56505f26f7 (patch)
tree12b07c7e578fc1a5f53dbfde2632408491ff2a70 /chips/scc_8530_hdw.c
Initial source
Diffstat (limited to 'chips/scc_8530_hdw.c')
-rw-r--r--chips/scc_8530_hdw.c1145
1 files changed, 1145 insertions, 0 deletions
diff --git a/chips/scc_8530_hdw.c b/chips/scc_8530_hdw.c
new file mode 100644
index 0000000..68c6e9d
--- /dev/null
+++ b/chips/scc_8530_hdw.c
@@ -0,0 +1,1145 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993-1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: scc_8530_hdw.c
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 6/91
+ *
+ * Hardware-level operations for the SCC Serial Line Driver
+ */
+
+#include <scc.h>
+#if NSCC > 0
+#include <bm.h>
+#include <platforms.h>
+
+#include <mach_kdb.h>
+
+#include <machine/machspl.h> /* spl definitions */
+#include <mach/std_types.h>
+#include <device/io_req.h>
+#include <device/tty.h>
+
+#include <chips/busses.h>
+#include <chips/serial_defs.h>
+#include <chips/screen_defs.h>
+
+/* Alignment and padding */
+#if defined(DECSTATION)
+/*
+ * 3min's padding
+ */
+typedef struct {
+ char pad0;
+ volatile unsigned char datum;
+ char pad1[2];
+} scc_padded1_register_t;
+
+#define scc_register_t scc_padded1_register_t
+#endif
+
+#if defined(FLAMINGO)
+typedef struct {
+ volatile unsigned int datum;
+ unsigned int pad1;
+} scc_padded1_register_t;
+
+#define scc_register_t scc_padded1_register_t
+
+#define scc_set_datum(d,v) (d) = (volatile unsigned int) (v) << 8, wbflush()
+#define scc_get_datum(d,v) (v) = ((d) >> 8) & 0xff
+
+#endif
+
+#include <chips/scc_8530.h> /* needs the above defs */
+
+#define private static
+#define public
+
+/*
+ * Forward decls
+ */
+private check_car( struct tty *, boolean_t );
+
+/*
+ * On the 3min keyboard and mouse come in on channels A
+ * of the two units. The MI code expects them at 'lines'
+ * 0 and 1, respectively. So we map here back and forth.
+ * Note also the MI code believes unit 0 has four lines.
+ */
+
+#define SCC_KBDUNIT 1
+#define SCC_PTRUNIT 0
+
+mi_to_scc(unitp, linep)
+ int *unitp, *linep;
+{
+ /* only play games on MI 'unit' 0 */
+ if (*unitp) {
+ /* e.g. by mapping the first four lines specially */
+ *unitp++;
+ return;
+ }
+
+ /* always get unit=0 (console) and line = 0|1 */
+ if (*linep == SCREEN_LINE_KEYBOARD) {
+ *unitp = SCC_KBDUNIT;
+ *linep = SCC_CHANNEL_A;
+ } else if (*linep == SCREEN_LINE_POINTER) {
+ *unitp = SCC_PTRUNIT;
+ *linep = SCC_CHANNEL_A;
+ } else {
+ *unitp = (*linep & 1);
+ *linep = SCC_CHANNEL_B;
+ }
+/* line 0 is channel B, line 1 is channel A */
+}
+
+#define NSCC_LINE 2 /* 2 ttys per chip */
+
+/* only care for mapping to ttyno */
+scc_to_mi(sccunit, sccline)
+{
+ if (sccunit > 1)
+ return (sccunit * NSCC_LINE + sccline);
+ /* only for console (first pair of SCCs): */
+ if (sccline == SCC_CHANNEL_A)
+ return ((!sccunit) & 1);
+ return 2+sccunit;
+}
+
+
+/*
+ * Driver status
+ */
+struct scc_softc {
+ scc_regmap_t *regs;
+
+ /* software copy of some write regs, for reg |= */
+ struct softreg {
+ unsigned char wr1;
+ unsigned char wr4;
+ unsigned char wr5;
+ unsigned char wr14;
+ } softr[2]; /* per channel */
+
+ unsigned char last_rr0[2]; /* for modem signals */
+ unsigned short fake; /* missing rs232 bits, channel A */
+ char polling_mode;
+ char softCAR, osoftCAR;
+ char probed_once;
+
+ boolean_t full_modem;
+ boolean_t isa_console;
+
+} scc_softc_data[NSCC];
+
+typedef struct scc_softc *scc_softc_t;
+
+scc_softc_t scc_softc[NSCC];
+
+scc_softCAR(unit, line, on)
+{
+ mi_to_scc(&unit, &line);
+ if (on)
+ scc_softc[unit]->softCAR |= 1<<line;
+ else
+ scc_softc[unit]->softCAR &= ~(1 << line);
+}
+
+
+/*
+ * BRG formula is:
+ * ClockFrequency
+ * BRGconstant = --------------------------- - 2
+ * 2 * BaudRate * ClockDivider
+ */
+/* Speed selections with Pclk=7.3728Mhz, clock x16 */
+static
+short scc_speeds[] =
+ /* 0 50 75 110 134.5 150 200 300 600 1200 1800 2400 */
+ { 0, 4606, 3070, 2093, 1711, 1534, 1150, 766, 382, 190, 126, 94,
+
+ /* 4800 9600 19.2k 38.4k */
+ 46, 22, 10, 4};
+
+/*
+ * Definition of the driver for the auto-configuration program.
+ */
+
+int scc_probe(), scc_intr();
+static void scc_attach();
+
+vm_offset_t scc_std[NSCC] = { 0 };
+struct bus_device *scc_info[NSCC];
+struct bus_driver scc_driver =
+ { scc_probe, 0, scc_attach, 0, scc_std, "scc", scc_info,};
+
+/*
+ * Adapt/Probe/Attach functions
+ */
+boolean_t scc_uses_modem_control = FALSE;/* patch this with adb */
+
+set_scc_address(
+ int sccunit,
+ vm_offset_t regs,
+ boolean_t has_modem,
+ boolean_t isa_console)
+{
+ extern int scc_probe(), scc_param(), scc_start(),
+ scc_putc(), scc_getc(),
+ scc_pollc(), scc_mctl(), scc_softCAR();
+
+ scc_std[sccunit] = regs;
+ scc_softc_data[sccunit].full_modem = has_modem & scc_uses_modem_control;
+ scc_softc_data[sccunit].isa_console = isa_console;
+
+ /* Do this here */
+ console_probe = scc_probe;
+ console_param = scc_param;
+ console_start = scc_start;
+ console_putc = scc_putc;
+ console_getc = scc_getc;
+ console_pollc = scc_pollc;
+ console_mctl = scc_mctl;
+ console_softCAR = scc_softCAR;
+
+}
+
+scc_probe(
+ int xxx,
+ struct bus_device *ui)
+{
+ int sccunit = ui->unit;
+ scc_softc_t scc;
+ register int val;
+ register scc_regmap_t *regs;
+
+ regs = (scc_regmap_t *)scc_std[sccunit];
+ if (regs == 0)
+ return 0;
+
+ /*
+ * See if we are here
+ */
+ if (check_memory(regs, 0)) {
+ /* no rides today */
+ return 0;
+ }
+
+ scc = &scc_softc_data[sccunit];
+
+ if (scc->probed_once++){
+ return 1;
+ }
+ /*
+ * Chip once-only initialization
+ *
+ * NOTE: The wiring we assume is the one on the 3min:
+ *
+ * out A-TxD --> TxD keybd or mouse
+ * in A-RxD --> RxD keybd or mouse
+ * out A-DTR~ --> DTR comm
+ * out A-RTS~ --> RTS comm
+ * in A-CTS~ --> SI comm
+ * in A-DCD~ --> RI comm
+ * in A-SYNCH~--> DSR comm
+ * out B-TxD --> TxD comm
+ * in B-RxD --> RxD comm
+ * in B-RxC --> TRxCB comm
+ * in B-TxC --> RTxCB comm
+ * out B-RTS~ --> SS comm
+ * in B-CTS~ --> CTS comm
+ * in B-DCD~ --> CD comm
+ */
+
+ scc_softc[sccunit] = scc;
+ scc->regs = regs;
+
+ scc->fake = 1<<SCC_CHANNEL_A;
+
+ {
+ register int i;
+ /* We need this in scc_start only, hence the funny
+ value: we need it non-zero and we want to avoid
+ too much overhead in getting to (scc,regs,line) */
+ for (i = 0; i < NSCC_LINE; i++) {
+ register struct tty *tp;
+
+ tp = console_tty[scc_to_mi(sccunit,i)];
+ tp->t_addr = (char*)(0x80000000L + (sccunit<<1) + (i&1));
+ /* do min buffering */
+ tp->t_state |= TS_MIN;
+ }
+ }
+
+ /* make sure reg pointer is in known state */
+ scc_init_reg(regs, SCC_CHANNEL_A);
+ scc_init_reg(regs, SCC_CHANNEL_B);
+
+ /* reset chip, fully */
+ scc_write_reg(regs, SCC_CHANNEL_A, SCC_WR9, SCC_WR9_HW_RESET);
+ delay(50000);/*enough ? */
+ scc_write_reg(regs, SCC_CHANNEL_A, SCC_WR9, 0);
+
+ /* program the interrupt vector */
+ scc_write_reg(regs, SCC_CHANNEL_A, SCC_WR2, 0xf0);
+ scc_write_reg(regs, SCC_CHANNEL_B, SCC_WR2, 0xf0);
+ scc_write_reg(regs, SCC_CHANNEL_A, SCC_WR9, SCC_WR9_VIS);
+
+ /* most of the init is in scc_param() */
+
+ /* timing base defaults */
+ scc->softr[SCC_CHANNEL_A].wr4 = SCC_WR4_CLK_x16;
+ scc->softr[SCC_CHANNEL_B].wr4 = SCC_WR4_CLK_x16;
+
+ /* enable DTR, RTS and dont SS */
+#if 0
+ /* According to one book I have this signal (pin 23, "SS")
+ is "specified by the provider", meaning the EIA-232-D
+ standard does not define what it is. Better leave
+ it alone */
+ scc->softr[SCC_CHANNEL_B].wr5 = SCC_WR5_RTS;
+#else
+ scc->softr[SCC_CHANNEL_B].wr5 = 0;
+#endif
+ scc->softr[SCC_CHANNEL_A].wr5 = SCC_WR5_RTS | SCC_WR5_DTR;
+
+ /* baud rates */
+ val = SCC_WR14_BAUDR_ENABLE|SCC_WR14_BAUDR_SRC;
+ scc->softr[SCC_CHANNEL_B].wr14 = val;
+ scc->softr[SCC_CHANNEL_A].wr14 = val;
+
+ /* interrupt conditions */
+ val = SCC_WR1_RXI_ALL_CHAR | SCC_WR1_PARITY_IE |
+ SCC_WR1_EXT_IE | SCC_WR1_TX_IE;
+ scc->softr[SCC_CHANNEL_A].wr1 = val;
+ scc->softr[SCC_CHANNEL_B].wr1 = val;
+
+ scc_read_reg_zero(regs, SCC_CHANNEL_A, scc->last_rr0[SCC_CHANNEL_A]);
+ scc_read_reg_zero(regs, SCC_CHANNEL_B, scc->last_rr0[SCC_CHANNEL_B]);
+
+ /*
+ * After probing, any line that should be active
+ * (keybd,mouse,rcline) is activated via scc_param().
+ */
+
+ scc_set_modem_control(scc, scc->full_modem);
+
+#if defined(KMIN) || defined (FLAMINGO) || defined(KN03)
+ /*
+ * Crock: MI code knows of unit 0 as console, we need
+ * unit 1 as well since the keyboard is there
+ * This is acceptable on maxine, which has to call its
+ * only one chip unit 1 so that rconsole is happy.
+ */
+ if (sccunit == 0) {
+ struct bus_device d;
+ d = *ui;
+ d.unit = 1;
+ scc_probe( xxx, &d);
+ }
+#endif
+ return 1;
+}
+
+boolean_t scc_timer_started = FALSE;
+
+static void
+scc_attach(
+ register struct bus_device *ui)
+{
+ int sccunit = ui->unit;
+ extern scc_scan();
+ extern int tty_inq_size;
+ int i;
+
+ /* We only have 4 ttys, but always at 9600
+ * Give em a lot of room (plus dma..)
+ */
+ tty_inq_size = 4096;
+ if (!scc_timer_started) {
+ /* do all of them, before we call scc_scan() */
+ /* harmless if done already */
+ for (i = 0; i < NSCC*NSCC_LINE; i++)
+ ttychars(console_tty[i]);
+
+ scc_timer_started = TRUE;
+ scc_scan();
+ }
+
+#if NBM > 0
+ if (SCREEN_ISA_CONSOLE() && scc_softc[sccunit]->isa_console) {
+ printf("\n sl0: ");
+ if (sccunit && rcline == 3) printf("( rconsole )");
+
+ if (sccunit == SCC_KBDUNIT) {
+ printf("\n sl1: "); lk201_attach(0, sccunit >> 1);
+ } else if (sccunit == SCC_PTRUNIT) {
+ printf("\n sl1: "); mouse_attach(0, sccunit >> 1);
+ }
+ } else
+#endif /*NBM > 0*/
+ {
+ printf("%s", (sccunit == 1) ?
+ "\n sl0: ( alternate console )\n sl1:" :
+ "\n sl0:\n sl1:");
+ }
+}
+
+/*
+ * Would you like to make a phone call ?
+ */
+scc_set_modem_control(
+ scc_softc_t scc,
+ boolean_t on)
+{
+ if (on)
+ /* your problem if the hardware then is broke */
+ scc->fake = 0;
+ else
+ scc->fake = 3;
+ scc->full_modem = on;
+ /* user should do an scc_param() ifchanged */
+}
+
+/*
+ * Polled I/O (debugger)
+ */
+scc_pollc(
+ int unit,
+ boolean_t on)
+{
+ scc_softc_t scc;
+ int line = SCREEN_LINE_KEYBOARD,
+ sccunit = unit;
+
+ mi_to_scc(&sccunit, &line);
+
+ scc = scc_softc[sccunit];
+ if (on) {
+ scc->polling_mode++;
+#if NBM > 0
+ screen_on_off(unit, TRUE);
+#endif NBM > 0
+ } else
+ scc->polling_mode--;
+}
+
+/*
+ * Interrupt routine
+ */
+int scc_intr_count;
+
+scc_intr(
+ int unit,
+ spl_t spllevel)
+{
+ scc_softc_t scc = scc_softc[unit];
+ register scc_regmap_t *regs = scc->regs;
+ register int rr1, rr2;
+ register int c;
+
+scc_intr_count++;
+
+#if mips
+ splx(spllevel); /* lower priority */
+#endif
+
+ while (1) {
+
+ scc_read_reg(regs, SCC_CHANNEL_B, SCC_RR2, rr2);
+
+ rr2 = SCC_RR2_STATUS(rr2);
+
+ /* are we done yet ? */
+ if (rr2 == 6) { /* strange, distinguished value */
+ register int rr3;
+ scc_read_reg(regs, SCC_CHANNEL_A, SCC_RR3, rr3);
+ if (rr3 == 0)
+ return;
+ }
+
+ if ((rr2 == SCC_RR2_A_XMIT_DONE) || (rr2 == SCC_RR2_B_XMIT_DONE)) {
+
+ register chan = (rr2 == SCC_RR2_A_XMIT_DONE) ?
+ SCC_CHANNEL_A : SCC_CHANNEL_B;
+
+ scc_write_reg(regs, SCC_CHANNEL_A, SCC_RR0, SCC_RESET_HIGHEST_IUS);
+ c = cons_simple_tint(scc_to_mi(unit,chan), FALSE);
+
+ if (c == -1) {
+ /* no more data for this line */
+
+ scc_read_reg(regs, chan, SCC_RR15, c);
+ c &= ~SCC_WR15_TX_UNDERRUN_IE;
+ scc_write_reg(regs, chan, SCC_WR15, c);
+
+ c = scc->softr[chan].wr1 & ~SCC_WR1_TX_IE;
+ scc_write_reg(regs, chan, SCC_WR1, c);
+ scc->softr[chan].wr1 = c;
+
+ c = cons_simple_tint(scc_to_mi(unit,chan), TRUE);
+ if (c != -1)
+ /* funny race, scc_start has been called already */
+ scc_write_data(regs, chan, c);
+ } else {
+ scc_write_data(regs, chan, c);
+ /* and leave it enabled */
+ }
+ }
+
+ else if (rr2 == SCC_RR2_A_RECV_DONE) {
+ int err = 0;
+
+ scc_write_reg(regs, SCC_CHANNEL_A, SCC_RR0, SCC_RESET_HIGHEST_IUS);
+ if (scc->polling_mode)
+ continue;
+
+ scc_read_data(regs, SCC_CHANNEL_A, c);
+ rr1 = scc_to_mi(unit,SCC_CHANNEL_A);
+ cons_simple_rint (rr1, rr1, c, 0);
+ }
+
+ else if (rr2 == SCC_RR2_B_RECV_DONE) {
+ int err = 0;
+
+ scc_write_reg(regs, SCC_CHANNEL_A, SCC_RR0, SCC_RESET_HIGHEST_IUS);
+ if (scc->polling_mode)
+ continue;
+
+ scc_read_data(regs, SCC_CHANNEL_B, c);
+ rr1 = scc_to_mi(unit,SCC_CHANNEL_B);
+ cons_simple_rint (rr1, rr1, c, 0);
+ }
+
+ else if ((rr2 == SCC_RR2_A_EXT_STATUS) || (rr2 == SCC_RR2_B_EXT_STATUS)) {
+ int chan = (rr2 == SCC_RR2_A_EXT_STATUS) ?
+ SCC_CHANNEL_A : SCC_CHANNEL_B;
+ scc_write_reg(regs, chan, SCC_RR0, SCC_RESET_EXT_IP);
+ scc_write_reg(regs, SCC_CHANNEL_A, SCC_RR0, SCC_RESET_HIGHEST_IUS);
+ scc_modem_intr(scc, chan, unit);
+ }
+
+ else if ((rr2 == SCC_RR2_A_RECV_SPECIAL) || (rr2 == SCC_RR2_B_RECV_SPECIAL)) {
+ register int chan = (rr2 == SCC_RR2_A_RECV_SPECIAL) ?
+ SCC_CHANNEL_A : SCC_CHANNEL_B;
+
+ scc_read_reg(regs, chan, SCC_RR1, rr1);
+ if (rr1 & (SCC_RR1_PARITY_ERR | SCC_RR1_RX_OVERRUN | SCC_RR1_FRAME_ERR)) {
+ int err;
+ /* map to CONS_ERR_xxx MI error codes */
+ err = ((rr1 & SCC_RR1_PARITY_ERR)<<8) |
+ ((rr1 & SCC_RR1_RX_OVERRUN)<<9) |
+ ((rr1 & SCC_RR1_FRAME_ERR)<<7);
+ scc_write_reg(regs, chan, SCC_RR0, SCC_RESET_ERROR);
+ rr1 = scc_to_mi(unit,chan);
+ cons_simple_rint(rr1, rr1, 0, err);
+ }
+ scc_write_reg(regs, SCC_CHANNEL_A, SCC_RR0, SCC_RESET_HIGHEST_IUS);
+ }
+
+ }
+
+}
+
+boolean_t
+scc_start(
+ struct tty *tp)
+{
+ register scc_regmap_t *regs;
+ register int chan, temp;
+ register struct softreg *sr;
+
+ temp = (natural_t)tp->t_addr;
+ chan = (temp & 1); /* channel */
+ temp = (temp >> 1)&0xff;/* sccunit */
+ regs = scc_softc[temp]->regs;
+ sr = &scc_softc[temp]->softr[chan];
+
+ scc_read_reg(regs, chan, SCC_RR15, temp);
+ temp |= SCC_WR15_TX_UNDERRUN_IE;
+ scc_write_reg(regs, chan, SCC_WR15, temp);
+
+ temp = sr->wr1 | SCC_WR1_TX_IE;
+ scc_write_reg(regs, chan, SCC_WR1, temp);
+ sr->wr1 = temp;
+
+ /* but we need a first char out or no cookie */
+ scc_read_reg(regs, chan, SCC_RR0, temp);
+ if (temp & SCC_RR0_TX_EMPTY)
+ {
+ register char c;
+
+ c = getc(&tp->t_outq);
+ scc_write_data(regs, chan, c);
+ }
+}
+
+/*
+ * Get a char from a specific SCC line
+ * [this is only used for console&screen purposes]
+ */
+scc_getc(
+ int unit,
+ int line,
+ boolean_t wait,
+ boolean_t raw)
+{
+ scc_softc_t scc;
+ register scc_regmap_t *regs;
+ unsigned char c;
+ int value, mi_line, rcvalue, from_line;
+
+ mi_line = line;
+ mi_to_scc(&unit, &line);
+
+ scc = scc_softc[unit];
+ regs = scc->regs;
+
+ /*
+ * wait till something available
+ *
+ * NOTE: we know! that rcline==3
+ */
+ if (rcline) rcline = 3;
+again:
+ rcvalue = 0;
+ while (1) {
+ scc_read_reg_zero(regs, line, value);
+ if (rcline && (mi_line == SCREEN_LINE_KEYBOARD)) {
+ scc_read_reg_zero(regs, SCC_CHANNEL_B, rcvalue);
+ value |= rcvalue;
+ }
+ if (((value & SCC_RR0_RX_AVAIL) == 0) && wait)
+ delay(10);
+ else
+ break;
+ }
+
+ /*
+ * if nothing found return -1
+ */
+ from_line = (rcvalue & SCC_RR0_RX_AVAIL) ? SCC_CHANNEL_B : line;
+
+ if (value & SCC_RR0_RX_AVAIL) {
+ scc_read_reg(regs, from_line, SCC_RR1, value);
+ scc_read_data(regs, from_line, c);
+ } else {
+/* splx(s);*/
+ return -1;
+ }
+
+ /*
+ * bad chars not ok
+ */
+ if (value&(SCC_RR1_PARITY_ERR | SCC_RR1_RX_OVERRUN | SCC_RR1_FRAME_ERR)) {
+/* scc_state(unit,from_line); */
+ scc_write_reg(regs, from_line, SCC_RR0, SCC_RESET_ERROR);
+ if (wait) {
+ scc_write_reg(regs, SCC_CHANNEL_A, SCC_RR0, SCC_RESET_HIGHEST_IUS);
+ goto again;
+ }
+ }
+ scc_write_reg(regs, SCC_CHANNEL_A, SCC_RR0, SCC_RESET_HIGHEST_IUS);
+/* splx(s);*/
+
+
+#if NBM > 0
+ if ((mi_line == SCREEN_LINE_KEYBOARD) && (from_line == SCC_CHANNEL_A) &&
+ !raw && SCREEN_ISA_CONSOLE() && scc->isa_console)
+ return lk201_rint(SCREEN_CONS_UNIT(), c, wait, scc->polling_mode);
+ else
+#endif NBM > 0
+ return c;
+}
+
+/*
+ * Put a char on a specific SCC line
+ */
+scc_putc(
+ int unit,
+ int line,
+ int c)
+{
+ scc_softc_t scc;
+ register scc_regmap_t *regs;
+ spl_t s = spltty();
+ register int value;
+
+ mi_to_scc(&unit, &line);
+
+ scc = scc_softc[unit];
+ regs = scc->regs;
+
+ do {
+ scc_read_reg(regs, line, SCC_RR0, value);
+ if (value & SCC_RR0_TX_EMPTY)
+ break;
+ delay(100);
+ } while (1);
+
+ scc_write_data(regs, line, c);
+/* wait for it to swallow the char ? */
+
+ splx(s);
+}
+
+scc_param(
+ struct tty *tp,
+ int line)
+{
+ scc_regmap_t *regs;
+ int value, sccline, unit;
+ struct softreg *sr;
+ scc_softc_t scc;
+
+ line = tp->t_dev;
+ /* MI code wants us to handle 4 lines on unit 0 */
+ unit = (line < 4) ? 0 : (line / NSCC_LINE);
+ sccline = line;
+ mi_to_scc(&unit, &sccline);
+
+ if ((scc = scc_softc[unit]) == 0) return; /* sanity */
+ regs = scc->regs;
+
+ sr = &scc->softr[sccline];
+
+ /*
+ * Do not let user fool around with kbd&mouse
+ */
+#if NBM > 0
+ if (screen_captures(line)) {
+ tp->t_ispeed = tp->t_ospeed = B4800;
+ tp->t_flags |= TF_LITOUT;
+ }
+#endif NBM > 0
+
+ if (tp->t_ispeed == 0) {
+ (void) scc_mctl(tp->t_dev, TM_HUP, DMSET); /* hang up line */
+ return;
+ }
+
+ /* reset line */
+ value = (sccline == SCC_CHANNEL_A) ? SCC_WR9_RESET_CHA_A : SCC_WR9_RESET_CHA_B;
+ scc_write_reg(regs, sccline, SCC_WR9, value);
+ delay(25);
+
+ /* stop bits, normally 1 */
+ value = sr->wr4 & 0xf0;
+ value |= (tp->t_ispeed == B110) ? SCC_WR4_2_STOP : SCC_WR4_1_STOP;
+
+ /* .. and parity */
+ if ((tp->t_flags & (TF_ODDP | TF_EVENP)) == TF_ODDP)
+ value |= SCC_WR4_PARITY_ENABLE;
+
+ /* set it now, remember it must be first after reset */
+ sr->wr4 = value;
+ scc_write_reg(regs, sccline, SCC_WR4, value);
+
+ /* vector again */
+ scc_write_reg(regs, sccline, SCC_WR2, 0xf0);
+
+ /* we only do 8 bits per char */
+ value = SCC_WR3_RX_8_BITS;
+ scc_write_reg(regs, sccline, SCC_WR3, value);
+
+ /* clear break, keep rts dtr */
+ value = sr->wr5 & (SCC_WR5_DTR|SCC_WR5_RTS);
+ value |= SCC_WR5_TX_8_BITS;
+ sr->wr5 = value;
+ scc_write_reg(regs, sccline, SCC_WR5, value);
+ /* some are on the other channel, which might
+ never be used (e.g. maxine has only one line) */
+ {
+ register int otherline = (sccline+1)&1;
+
+ scc_write_reg(regs, otherline, SCC_WR5, scc->softr[otherline].wr5);
+ }
+
+ scc_write_reg(regs, sccline, SCC_WR6, 0);
+ scc_write_reg(regs, sccline, SCC_WR7, 0);
+
+ scc_write_reg(regs, sccline, SCC_WR9, SCC_WR9_VIS);
+
+ scc_write_reg(regs, sccline, SCC_WR10, 0);
+
+ /* clock config */
+ value = SCC_WR11_RCLK_BAUDR | SCC_WR11_XTLK_BAUDR |
+ SCC_WR11_TRc_OUT | SCC_WR11_TRcOUT_BAUDR;
+ scc_write_reg(regs, sccline, SCC_WR11, value);
+
+ value = scc_speeds[tp->t_ispeed];
+ scc_set_timing_base(regs,sccline,value);
+
+ value = sr->wr14;
+ scc_write_reg(regs, sccline, SCC_WR14, value);
+
+#if FLAMINGO
+ if (unit != 1)
+#else
+ if (1)
+#endif
+ {
+ /* Chan-A: CTS==SI DCD==RI DSR=SYNCH */
+ value = SCC_WR15_CTS_IE | SCC_WR15_DCD_IE | SCC_WR15_SYNCHUNT_IE;
+ scc_write_reg(regs, SCC_CHANNEL_A, SCC_WR15, value);
+
+ /* Chan-B: CTS==CTS DCD==DCD */
+ value = SCC_WR15_BREAK_IE | SCC_WR15_CTS_IE | SCC_WR15_DCD_IE;
+ scc_write_reg(regs, SCC_CHANNEL_B, SCC_WR15, value);
+ } else {
+ /* Here if modem bits are floating noise, keep quiet */
+ value = SCC_WR15_BREAK_IE;
+ scc_write_reg(regs, sccline, SCC_WR15, value);
+ }
+
+ /* and now the enables */
+ value = SCC_WR3_RX_8_BITS | SCC_WR3_RX_ENABLE;
+ scc_write_reg(regs, sccline, SCC_WR3, value);
+
+ value = sr->wr5 | SCC_WR5_TX_ENABLE;
+ sr->wr5 = value;
+ scc_write_reg(regs, sccline, SCC_WR5, value);
+
+ /* master inter enable */
+ scc_write_reg(regs,sccline,SCC_WR9,SCC_WR9_MASTER_IE|SCC_WR9_VIS);
+
+ scc_write_reg(regs, sccline, SCC_WR1, sr->wr1);
+
+}
+
+/*
+ * Modem control functions
+ */
+scc_mctl(
+ int dev,
+ int bits,
+ int how)
+{
+ register scc_regmap_t *regs;
+ struct softreg *sra, *srb, *sr;
+ int unit, sccline;
+ int b = 0;
+ spl_t s;
+ scc_softc_t scc;
+
+ /* MI code wants us to handle 4 lines on unit 0 */
+ unit = (dev < 4) ? 0 : (dev / NSCC_LINE);
+ sccline = dev;
+ mi_to_scc(&unit, &sccline);
+
+ if ((scc = scc_softc[unit]) == 0) return 0; /* sanity */
+ regs = scc->regs;
+
+ sr = &scc->softr[sccline];
+ sra = &scc->softr[SCC_CHANNEL_A];
+ srb = &scc->softr[SCC_CHANNEL_B];
+
+ if (bits == TM_HUP) { /* close line (internal) */
+ bits = TM_DTR | TM_RTS;
+ how = DMBIC;
+ /* xxx interrupts too ?? */
+ }
+
+ if (bits & TM_BRK) {
+ switch (how) {
+ case DMSET:
+ case DMBIS:
+ sr->wr5 |= SCC_WR5_SEND_BREAK;
+ break;
+ case DMBIC:
+ sr->wr5 &= ~SCC_WR5_SEND_BREAK;
+ break;
+ default:
+ goto dontbrk;
+ }
+ s = spltty();
+ scc_write_reg(regs, sccline, SCC_WR5, sr->wr5);
+ splx(s);
+dontbrk:
+ b |= (sr->wr5 & SCC_WR5_SEND_BREAK) ? TM_BRK : 0;
+ }
+
+ /* no modem support on channel A */
+ if (sccline == SCC_CHANNEL_A)
+ return (b | TM_LE | TM_DTR | TM_CTS | TM_CAR | TM_DSR);
+
+ sra = &scc->softr[SCC_CHANNEL_A];
+ srb = &scc->softr[SCC_CHANNEL_B];
+
+#if 0
+ /* do I need to do something on this ? */
+ if (bits & TM_LE) { /* line enable */
+ }
+#endif
+
+ if (bits & (TM_DTR|TM_RTS)) { /* data terminal ready, request to send */
+ register int w = 0;
+
+ if (bits & TM_DTR) w |= SCC_WR5_DTR;
+ if (bits & TM_RTS) w |= SCC_WR5_RTS;
+
+ switch (how) {
+ case DMSET:
+ case DMBIS:
+ sra->wr5 |= w;
+ break;
+ case DMBIC:
+ sra->wr5 &= ~w;
+ break;
+ default:
+ goto dontdtr;
+ }
+ s = spltty();
+ scc_write_reg(regs, SCC_CHANNEL_A, SCC_WR5, sra->wr5);
+ splx(s);
+dontdtr:
+ b |= (sra->wr5 & w) ? (bits & (TM_DTR|TM_RTS)) : 0;
+ }
+
+ s = spltty();
+
+#if 0
+ /* Unsupported */
+ if (bits & TM_ST) { /* secondary transmit */
+ }
+ if (bits & TM_SR) { /* secondary receive */
+ }
+#endif
+
+ if (bits & TM_CTS) { /* clear to send */
+ register int value;
+ scc_read_reg(regs, SCC_CHANNEL_B, SCC_RR0, value);
+ b |= (value & SCC_RR0_CTS) ? TM_CTS : 0;
+ }
+
+ if (bits & TM_CAR) { /* carrier detect */
+ register int value;
+ scc_read_reg(regs, SCC_CHANNEL_B, SCC_RR0, value);
+ b |= (value & SCC_RR0_DCD) ? TM_CAR : 0;
+ }
+
+ if (bits & TM_RNG) { /* ring */
+ register int value;
+ scc_read_reg(regs, SCC_CHANNEL_A, SCC_RR0, value);
+ b |= (value & SCC_RR0_DCD) ? TM_RNG : 0;
+ }
+
+ if (bits & TM_DSR) { /* data set ready */
+ register int value;
+ scc_read_reg(regs, SCC_CHANNEL_A, SCC_RR0, value);
+ b |= (value & SCC_RR0_SYNCH) ? TM_DSR : 0;
+ }
+
+ splx(s);
+
+ return b;
+}
+
+#define debug 0
+
+scc_modem_intr(
+ scc_softc_t scc,
+ int chan,
+ int unit)
+{
+ register int value, changed;
+
+ scc_read_reg_zero(scc->regs, chan, value);
+
+ /* See what changed */
+ changed = value ^ scc->last_rr0[chan];
+ scc->last_rr0[chan] = value;
+
+#if debug
+printf("sccmodem: chan %c now %x, changed %x : ",
+ (chan == SCC_CHANNEL_B) ? 'B' : 'A',
+ value, changed);
+#endif
+
+ if (chan == SCC_CHANNEL_A) {
+ if (changed & SCC_RR0_CTS) {
+ /* Speed indicator, ignore XXX */
+#if debug
+printf("%s-speed ", (value & SCC_RR0_CTS) ? "Full" : "Half");
+#endif
+ }
+ if (changed & SCC_RR0_DCD) {
+ /* Ring indicator */
+#if debug
+printf("Ring ");
+#endif
+ }
+ if (changed & SCC_RR0_SYNCH) {
+ /* Data Set Ready */
+#if debug
+printf("DSR ");
+#endif
+ /* If modem went down then CD will also go down,
+ or it did already.
+ If modem came up then we have to wait for CD
+ anyways before enabling the line.
+ Either way, nothing to do here */
+ }
+ } else {
+ if (changed & SCC_RR0_CTS) {
+ /* Clear To Send */
+#if debug
+printf("CTS ");
+#endif
+ tty_cts(console_tty[scc_to_mi(unit,chan)],
+ value & SCC_RR0_CTS);
+ }
+ if (changed & SCC_RR0_DCD) {
+#if debug
+printf("CD ");
+#endif
+ check_car(console_tty[scc_to_mi(unit,chan)],
+ value & SCC_RR0_DCD);
+ }
+ }
+#if debug
+printf(".\n");
+#endif
+}
+
+private check_car(
+ register struct tty *tp,
+ boolean_t car)
+
+{
+ if (car) {
+#if notyet
+ /* cancel modem timeout if need to */
+ if (car & (SCC_MSR_CD2 | SCC_MSR_CD3))
+ untimeout(scc_hup, (vm_offset_t)tp);
+#endif
+
+ /* I think this belongs in the MI code */
+ if (tp->t_state & TS_WOPEN)
+ tp->t_state |= TS_ISOPEN;
+ /* carrier present */
+ if ((tp->t_state & TS_CARR_ON) == 0)
+ (void)ttymodem(tp, 1);
+ } else if ((tp->t_state&TS_CARR_ON) && ttymodem(tp, 0) == 0)
+ scc_mctl( tp->t_dev, TM_DTR, DMBIC);
+}
+
+/*
+ * Periodically look at the CD signals:
+ * they do generate interrupts but we
+ * must fake them on channel A. We might
+ * also fake them on channel B.
+ */
+scc_scan()
+{
+ register i;
+ spl_t s = spltty();
+
+ for (i = 0; i < NSCC; i++) {
+ register scc_softc_t scc;
+ register int car;
+ register struct tty **tpp;
+
+ scc = scc_softc[i];
+ if (scc == 0)
+ continue;
+ car = scc->softCAR | scc->fake;
+
+ tpp = &console_tty[i * NSCC_LINE];
+
+ while (car) {
+ if (car & 1)
+ check_car(*tpp, 1);
+ tpp++;
+ car = car>>1;
+ }
+
+ }
+ splx(s);
+ timeout(scc_scan, (vm_offset_t)0, 5*hz);
+}
+
+
+#if debug
+scc_rr0(unit,chan)
+{
+ int val;
+ scc_read_reg_zero(scc_softc[unit]->regs, chan, val);
+ return val;
+}
+
+scc_rreg(unit,chan,n)
+{
+ int val;
+ scc_read_reg(scc_softc[unit]->regs, chan, n, val);
+ return val;
+}
+
+scc_wreg(unit,chan,n,val)
+{
+ scc_write_reg(scc_softc[unit]->regs, chan, n, val);
+}
+
+scc_state(unit,soft)
+{
+ int rr0, rr1, rr3, rr12, rr13, rr15;
+
+ rr0 = scc_rreg(unit, SCC_CHANNEL_A, SCC_RR0);
+ rr1 = scc_rreg(unit, SCC_CHANNEL_A, SCC_RR1);
+ rr3 = scc_rreg(unit, SCC_CHANNEL_A, SCC_RR3);
+ rr12 = scc_rreg(unit, SCC_CHANNEL_A, SCC_RR12);
+ rr13 = scc_rreg(unit, SCC_CHANNEL_A, SCC_RR13);
+ rr15 = scc_rreg(unit, SCC_CHANNEL_A, SCC_RR15);
+ printf("{%d intr, A: R0 %x R1 %x R3 %x baudr %x R15 %x}\n",
+ scc_intr_count, rr0, rr1, rr3,
+ (rr13 << 8) | rr12, rr15);
+
+ rr0 = scc_rreg(unit, SCC_CHANNEL_B, SCC_RR0);
+ rr1 = scc_rreg(unit, SCC_CHANNEL_B, SCC_RR1);
+ rr3 = scc_rreg(unit, SCC_CHANNEL_B, SCC_RR2);
+ rr12 = scc_rreg(unit, SCC_CHANNEL_B, SCC_RR12);
+ rr13 = scc_rreg(unit, SCC_CHANNEL_B, SCC_RR13);
+ rr15 = scc_rreg(unit, SCC_CHANNEL_B, SCC_RR15);
+ printf("{B: R0 %x R1 %x R2 %x baudr %x R15 %x}\n",
+ rr0, rr1, rr3,
+ (rr13 << 8) | rr12, rr15);
+
+ if (soft) {
+ struct softreg *sr;
+ sr = scc_softc[unit]->softr;
+ printf("{B: W1 %x W4 %x W5 %x W14 %x}",
+ sr->wr1, sr->wr4, sr->wr5, sr->wr14);
+ sr++;
+ printf("{A: W1 %x W4 %x W5 %x W14 %x}\n",
+ sr->wr1, sr->wr4, sr->wr5, sr->wr14);
+ }
+}
+
+#endif
+
+#endif NSCC > 0