1039 lines
24 KiB
C
1039 lines
24 KiB
C
|
/*
|
||
|
* 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.
|
||
|
*/
|
||
|
/*
|
||
|
* HISTORY
|
||
|
* $Log: scc_8530_hdw.c,v $
|
||
|
* Revision 1.1.1.1 1993/09/29 06:09:28 briggs
|
||
|
* The current MacBSD architecture sub-directory for NetBSD-0.9--more or
|
||
|
* less. It needs some help to get to be -current. Support for minimal
|
||
|
* hardware on the SE/30, II, IIx, and IIcx exists.
|
||
|
*
|
||
|
* Revision 2.9 93/03/26 17:58:01 mrt
|
||
|
* No minor()s, no dev_t.
|
||
|
* [93/03/18 af]
|
||
|
*
|
||
|
* Revision 2.8 93/03/09 10:52:21 danner
|
||
|
* GCC quiet.
|
||
|
* [93/03/07 13:29:58 af]
|
||
|
*
|
||
|
* Post-debugging lint.
|
||
|
* [93/03/05 af]
|
||
|
*
|
||
|
* Revision 2.7 93/02/05 08:05:17 danner
|
||
|
* Flamingo, full_modem and isa_console per-line.
|
||
|
* [93/02/04 af]
|
||
|
*
|
||
|
* Revision 2.6 93/01/14 17:21:44 danner
|
||
|
* static/extern cleanups.
|
||
|
* [93/01/14 danner]
|
||
|
*
|
||
|
* Proper spl typing.
|
||
|
* [92/11/30 af]
|
||
|
*
|
||
|
* Revision 2.5 92/05/05 10:04:59 danner
|
||
|
* Fixed how the interrupt routine plays with priorities.
|
||
|
* Ask for buffering on all lines.
|
||
|
* [92/05/04 11:15:43 af]
|
||
|
*
|
||
|
* Fixed for more than just rconsole-ing. Two bugs: the chip
|
||
|
* needs one char out to generate the xmit-empty (so it really
|
||
|
* is a xmit-done) interrupt, and the t_addr game was not played
|
||
|
* properly.
|
||
|
* Tested both on maxine and the two serial lines kmin has.
|
||
|
* [92/04/14 11:47:26 af]
|
||
|
*
|
||
|
* Revision 2.4 92/02/19 16:46:10 elf
|
||
|
* Uhmm, lotsa changes. Basically, got channel B working
|
||
|
* and made it possible to use it as rconsole line.
|
||
|
* Missing modem bitsies only.
|
||
|
* A joint Terri&Sandro feature presentation.
|
||
|
* [92/02/10 17:03:08 af]
|
||
|
*
|
||
|
* Revision 2.3 91/08/28 11:09:53 jsb
|
||
|
* Fixed scc_scan to actually check the tp->state each time,
|
||
|
* we are not notified when the MI code brutally zeroes it
|
||
|
* on close and so we cannot update our software CARrier.
|
||
|
* [91/08/27 16:18:05 af]
|
||
|
*
|
||
|
* Revision 2.2 91/08/24 11:52:54 af
|
||
|
* Created, from the Zilog specs:
|
||
|
* "Z8530 SCC Serial Communications Controller, Product Specification"
|
||
|
* in the "1983/84 Components Data Book" pp 409-429, September 1983
|
||
|
* Zilog, Campbell, CA 95008
|
||
|
* [91/06/28 af]
|
||
|
*
|
||
|
*/
|
||
|
#ident "$Id: scc_8530_hdw.c,v 1.1.1.1 1993/09/29 06:09:28 briggs Exp $"
|
||
|
/*
|
||
|
* 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/machparam.h> /* spl definitions */
|
||
|
#include <sys/types.h>
|
||
|
#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 */
|
||
|
|
||
|
/*
|
||
|
* 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 short breaks;
|
||
|
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_attach(), scc_intr();
|
||
|
|
||
|
caddr_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( sccunit, regs, has_modem, isa_console)
|
||
|
caddr_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( xxx, ui)
|
||
|
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 SS */
|
||
|
scc->softr[SCC_CHANNEL_B].wr5 = SCC_WR5_RTS;
|
||
|
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;
|
||
|
|
||
|
/*
|
||
|
* 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)
|
||
|
/*
|
||
|
* 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;
|
||
|
|
||
|
scc_attach(ui)
|
||
|
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, on)
|
||
|
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(unit, on)
|
||
|
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(unit,spllevel)
|
||
|
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
|
||
|
|
||
|
next_intr:
|
||
|
|
||
|
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)
|
||
|
goto next_intr;
|
||
|
|
||
|
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)
|
||
|
goto next_intr;
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
goto next_intr;
|
||
|
|
||
|
}
|
||
|
|
||
|
boolean_t
|
||
|
scc_start(tp)
|
||
|
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( unit, line, wait, raw )
|
||
|
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( unit, line, 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(tp, line)
|
||
|
struct tty *tp;
|
||
|
{
|
||
|
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 0
|
||
|
if ((tp->t_flags & (TF_ODDP | TF_EVENP)) == TF_EVENP)
|
||
|
value |= SCC_WR4_EVEN_PARITY;
|
||
|
value |= SCC_WR4_PARITY_ENABLE;
|
||
|
#else
|
||
|
if ((tp->t_flags & (TF_ODDP | TF_EVENP)) == TF_ODDP)
|
||
|
value |= SCC_WR4_PARITY_ENABLE;
|
||
|
#endif
|
||
|
|
||
|
/* 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);
|
||
|
|
||
|
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);
|
||
|
|
||
|
value = (scc->full_modem) ?
|
||
|
SCC_WR15_BREAK_IE | SCC_WR15_CTS_IE | SCC_WR15_DCD_IE :
|
||
|
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(dev, bits, how)
|
||
|
int dev;
|
||
|
int bits, how;
|
||
|
{
|
||
|
return 0;
|
||
|
#if 0
|
||
|
register scc_regmap_t *regs;
|
||
|
int unit, sccline;
|
||
|
register int tcr, msr, brk, n_tcr, n_brk;
|
||
|
int b;
|
||
|
spl_t s;
|
||
|
scc_softc_t sc;
|
||
|
|
||
|
unit = dev;
|
||
|
sccline = unit & 3;
|
||
|
unit >>= 2;
|
||
|
mi_to_scc(&unit, &sccline);
|
||
|
|
||
|
/* no modem support on channel A */
|
||
|
/* XXX break on 0&1 */
|
||
|
if (sccline == SCC_CHANNEL_A)
|
||
|
return TM_LE | TM_DTR | TM_CTS | TM_CAR | TM_DSR;
|
||
|
|
||
|
scc = scc_softc[unit];
|
||
|
regs = scc->regs;
|
||
|
s = spltty();
|
||
|
|
||
|
....some other day..
|
||
|
|
||
|
tcr = ((regs->scc_tcr | (scc->fake>>4)) & 0xf00) >> (8 + b*2);
|
||
|
brk = (scc->breaks >> (8 + (unit&3))) & 1; /* THE break bit */
|
||
|
|
||
|
n_tcr = (bits & (TM_RTS | TM_DTR)) >> 1;
|
||
|
n_brk = (bits & TM_BRK) >> 9;
|
||
|
|
||
|
/* break transitions, must 'send' a char out */
|
||
|
bits = (brk ^ n_brk) & 1;
|
||
|
|
||
|
switch (how) {
|
||
|
case DMSET:
|
||
|
tcr = n_tcr;
|
||
|
brk = n_brk;
|
||
|
break;
|
||
|
|
||
|
case DMBIS:
|
||
|
tcr |= n_tcr;
|
||
|
brk |= n_brk;
|
||
|
break;
|
||
|
|
||
|
case DMBIC:
|
||
|
tcr &= ~n_tcr;
|
||
|
brk = 0;
|
||
|
break;
|
||
|
|
||
|
case DMGET:
|
||
|
msr = ((regs->scc_msr | scc->fake) & 0xf0f) >> (b*8);
|
||
|
(void) splx(s);
|
||
|
return (tcr<<1)|/* DTR, RTS */
|
||
|
((msr&1)<<5)|/* CTS */
|
||
|
((msr&2)<<7)|/* DSR */
|
||
|
((msr&0xc)<<4)|/* CD, RNG */
|
||
|
(brk << 9)|/* BRK */
|
||
|
TM_LE;
|
||
|
}
|
||
|
n_tcr = (regs->scc_tcr & ~(3 << (8 + b*2))) |
|
||
|
(tcr << (8 + b*2));
|
||
|
|
||
|
regs->scc_tcr = n_tcr;
|
||
|
scc->fake = (scc->fake & 0xf0f) | (n_tcr<<4&0xf000);
|
||
|
|
||
|
scc->breaks = (scc->breaks & ~(1 << (8 + (unit&3)))) |
|
||
|
(brk << (8 + (unit&3)));
|
||
|
if(bits) scc_putc( unit>>2, unit&3, 0);/* force break, now */
|
||
|
(void) splx(s);
|
||
|
return 0;/* useless to compute it */
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
scc_modem_intr(scc, chan)
|
||
|
{
|
||
|
/* nothing yet */
|
||
|
}
|
||
|
|
||
|
static 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, (caddr_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, (caddr_t)0, 5*hz);
|
||
|
}
|
||
|
|
||
|
|
||
|
#if 0 /* debug */
|
||
|
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)
|
||
|
{
|
||
|
printf("{%d intr, A: R0 %x R1 %x R3 %x baudr %x R15 %x}\n",
|
||
|
scc_intr_count,
|
||
|
scc_rreg(unit, 1, SCC_RR0),
|
||
|
scc_rreg(unit, 1, SCC_RR1),
|
||
|
scc_rreg(unit, 1, SCC_RR3),
|
||
|
(scc_rreg(unit, 1, SCC_RR13) << 8) | scc_rreg(unit, 1, SCC_RR12),
|
||
|
scc_rreg(unit, 1, SCC_RR15));
|
||
|
printf("{B: R0 %x R1 %x R2 %x baudr %x R15 %x}\n",
|
||
|
scc_rreg(unit, 1, SCC_RR0),
|
||
|
scc_rreg(unit, 1, SCC_RR1),
|
||
|
scc_rreg(unit, 1, SCC_RR2),
|
||
|
(scc_rreg(unit, 1, SCC_RR13) << 8) | scc_rreg(unit, 1, SCC_RR12),
|
||
|
scc_rreg(unit, 1, SCC_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
|