diff options
Diffstat (limited to 'chips/scc_8530_hdw.c')
-rw-r--r-- | chips/scc_8530_hdw.c | 1145 |
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 00000000..68c6e9d2 --- /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 |