/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:psp.c 12.0$ */
/* $ACIS:psp.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/caio/RCS/psp.c,v $ */

#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid = "$Header:psp.c 12.0$";
#endif

/*
 *  Planar Serial Port Driver
 */

#include "psp.h"

#if NPSP > 0
#include "../h/param.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/ioctl.h"
#include "../h/tty.h"
#include "../h/systm.h"
#include "../h/uio.h"
#include "../h/file.h"
#include "../machine/debug.h"
#include "../machineio/pspreg.h"
#include "../machine/io.h"
#include "../h/buf.h"
#include "../machineio/ioccvar.h"
#include "ap.h"
#if NAP > 0
#include "../machineio/apdefs.h"
#endif
#include "../machine/softint.h"
#include "kernel.h"		/* for hz */

#define spl_psp() _spl3()

struct pspdevice psp_a = {
	(char *) 0xf0008001,	       /* control channel A */
	(char *) 0xf0008003,	       /* data    channel A */
	(char *) 0xf0008020,	       /* ext_reg channel A */
	(char *) 0xf0008060	       /* int_ack */
};

struct pspdevice psp_b = {
	(char *) 0xf0008000,	       /* control channel B */
	(char *) 0xf0008002,	       /* data    channel B */
	(char *) 0xf0008040,	       /* ext_reg channel B */
	(char *) 0xf0008060	       /* int_ack */
};

#define PSP_SETBAUD(addr, chan, baud) if ((baud) < EXTB) { \
	   psp_write_reg((addr), (chan), W14, W14_DIS_BDG); \
	   psp_write_reg((addr), (chan), W13, pspbaudtbl[(baud)].msb); \
	   psp_write_reg((addr), (chan), W12, pspbaudtbl[(baud)].lsb); \
	   psp_write_reg((addr), (chan), W14, W14_EN_BDG);  } 

static struct pspbaudtbl {
	unsigned char msb;
	unsigned char lsb;
} pspbaudtbl[NSPEEDS] = {
	/* B0    */  0x00, 0x00,
	/* B50   */  0x07, 0x7e,
	/* B75   */  0x04, 0xfe,
	/* B110  */  0x03, 0x66,
	/* B134  */  0x02, 0xca,
	/* B150  */  0x02, 0x7e,
	/* B200  */  0x01, 0xde,
	/* B300  */  0x01, 0x3e,
	/* B600  */  0x00, 0x9e,
	/* B1200 */  0x00, 0x4e,
	/* B1800 */  0x00, 0x33,
	/* B2400 */  0x00, 0x26,
	/* B4800 */  0x00, 0x12,
	/* B9600 */  0x00, 0x08,
	/* B19200 */ 0x00, 0x03	       
	/* EXTB 38400  not supported */
};

/* output rate at various speeds; division by 10 is to convert baud to 
 * characters per second */
static int pspoutputrate[] = {
	10/10, 50/10, 75/10, 110/10, 134/10, 150/10, 200/10, 300/10,
	600/10, 1200/10, 1800/10, 2400/10, 4800/10, 9600/10, 19200/10, 38400/10
	};

#define ISPEED		B9600			/* input and ouput speed */
#define NPORTS		2			/* Number of ports per card */
#define NREGS		16		        /* Number of registers */
#define MAXUNIT		(NPSP * NPORTS)		/* Maximum number of units */
#define PSP_CHANA	0		        /* psp channel A */
#define PSP_CHANB	1		        /* psp channel B */
#define UNITMASK(unit)	( 1 << ((unit) & 3) )
int 	npsp = MAXUNIT;

char pspsoftCAR;		       		/* carrier detect flags */
char psp_write_regs[MAXUNIT][NREGS];
struct tty psp[MAXUNIT];
struct cbuf pspcbuf[MAXUNIT];
struct clist pspxclist;
static int pspsavemsr = 0;

#if NAP > 0
int pspoutc();
#endif
int pspstart(), pspattach(), pspprobe(), pspint(), pspmint();
int ttrstrt();
char psp_read_reg();

struct iocc_device *pspdinfo[NPSP];

struct iocc_driver pspdriver = { 
	/* probe, slave, attach, dgo, addr, dname, dinfo, mname, csr, intr */
	pspprobe, 0, pspattach, 0, 0, "psp", pspdinfo, 0, 0, pspint
};

/* find an address which is safe to touch and put it in pspstd */
/* caddr_t pspstd[] = { 0, 0, 0 }; */

/*
 * Pspprobe will try to generate an interrupt so that the system can
 * determine whether or not the adapters specified in the config file
 * are truly present.
 */
/* ARGSUSED */
pspprobe(addr)
	register caddr_t addr;
{
	register struct pspdevice *ppsp_a = &psp_a;
	register struct pspdevice *ppsp_b = &psp_b;

	/* initialize the 8530 both channels */
	psp_write_reg(ppsp_a, PSP_CHANA, W9, W9_RST_HARD);/* hardware reset */
	psp_write_reg(ppsp_a, PSP_CHANA, W11, W11_SCLK);  /* Clock register */
	psp_write_reg(ppsp_b, PSP_CHANB, W11, W11_SCLK);  /* Clock register */
	psp_write_reg(ppsp_a, PSP_CHANA, W13, W13_B0); 	 /* load time const */
	psp_write_reg(ppsp_a, PSP_CHANA, W12, W12_B0);
	DEBUGF(pspdebug & SHOW_REGS, {  
		char high = psp_read_reg(ppsp_a, PSP_CHANA, R13);
		char low  = psp_read_reg(ppsp_a, PSP_CHANA, R12);
		printf( "high & low byte of time const=%x,%x\n", high, low); 
	} );
	/* enable master interrupt */
	psp_write_reg(ppsp_a, PSP_CHANA, W9, W9_EN_MI);    

	/* enable external interrupts */
	psp_write_reg(ppsp_a, PSP_CHANA, W1, W1_EN_EI);  

        /* enable zero count interrupts */
	psp_write_reg(ppsp_a, PSP_CHANA, W15, W15_EN_ZCI); 

        /* enable baud rate generation */
	psp_write_reg(ppsp_a, PSP_CHANA, W14, W14_EN_BDG);
	PROBE_DELAY(100000);

        /* disable baud rate generation */
	psp_write_reg(ppsp_a, PSP_CHANA, W14, W14_DIS_BDG);  
	*ppsp_a->int_ack = (char) 0;   		    /* interrupt acknowlege */

	/* reset the interrupt which we have generated */
	psp_write_reg(ppsp_a, PSP_CHANA, W0, W0_RST_IUS);
	psp_write_reg(ppsp_a, PSP_CHANA, W9, W9_RST_HARD);/* hardware reset */
	psp_write_reg(ppsp_a, PSP_CHANA, W11, W11_SCLK);  /* Clock register */
	psp_write_reg(ppsp_b, PSP_CHANB, W11, W11_SCLK);  /* Clock register */

	/* set baud to default speed */
	PSP_SETBAUD(ppsp_a, PSP_CHANA, ISPEED);
	PSP_SETBAUD(ppsp_b, PSP_CHANB, ISPEED);

	DEBUGF(pspdebug & SHOW_REGS, {  
		char high = psp_read_reg(ppsp_a, PSP_CHANA, R13);
		char low  = psp_read_reg(ppsp_a, PSP_CHANA, R12);
		printf("high & low byte of time const=%x,%x\n", high, low); 
		printf("Chan A:EXT/STAT IE bits=%x\n",psp_read_reg(ppsp_a,R15));
		printf("Chan B:EXT/STAT IE bits=%x\n",psp_read_reg(ppsp_b,R15));
	} );
	psp_write_reg(ppsp_a, PSP_CHANA, W9, W9_EN_MI);    
	return (PROBE_OK);
}


pspattach(iod)
	register struct iocc_device *iod;
{
	register struct tty *tp;
	register int port;

	DEBUGF(pspdebug & SHOW_INIT, printf("pspattach\n" ) );
	for (port = 0, tp = &psp[port] ; port < NPORTS; port++, tp++) { 
		tp->t_addr=(port) ? (caddr_t)&psp_b:(caddr_t)&psp_a;
		tp->t_cbufp = &pspcbuf[port];
		tp->t_state = 0;
	}
	pspsoftCAR = iod->iod_flags;   /* modem control flags  */
	DEBUGF(pspdebug & SHOW_REGS, {
		printf("Chan A:EXT/STAT IE bits=%x\n",psp_read_reg(&psp_a,R15));
		printf("Chan B:EXT/STAT IE bits=%x\n",psp_read_reg(&psp_b,R15));
	} );
}


/*ARGSUSED*/
pspopen(dev, flag)
	dev_t dev;
{
	register struct tty *tp;
	register int unit = minor(dev);
	register struct iocc_device *iod;
	register int s, error;
	register struct pspdevice *ppsp;

	DEBUGF(pspdebug & SHOW_OPEN, printf("pspopen%x: \n", unit));
	if (unit >= MAXUNIT || (iod=pspdinfo[0]) == 0 || iod->iod_alive==0)
		return (ENXIO);
	tp = &psp[unit];
	if (tp->t_state & TS_XCLUDE && u.u_uid != 0)
		return (EBUSY);

	ppsp = (struct pspdevice *)tp->t_addr;
	tp->t_state |= TS_WOPEN;

	if (tp->t_state & TS_CLOSE) {
		ttywait(tp);			/* wait til complete */
	}
	/* if this is the first open, initiallize tty state to defaults */
	if ((tp->t_state & TS_ISOPEN) == 0) {
		int x;
		pspsavemsr = 0;			/* start from scratch now */
		ttychars(tp);
		tp->t_oproc = pspstart;
		tp->t_ospeed = tp->t_ispeed = ISPEED;
		tp->t_flags = EVENP | ODDP | ECHO;
		pspparam(unit);
		s = spl_psp();
		psp_set_bits(ppsp, unit, W5, W5_RTS);  /* RTS (on the chip) */
		*ppsp->ext_reg = (char) EXT_DTR;               /* dtr on */
		psp_set_bits(ppsp, unit, W1, W1_RXI_ALL | W1_EN_EI);
		/* read status and character to clear old errors */
	    	x = psp_read_reg(ppsp, R1);		/* read status */
	    	x = *ppsp->data;		        /* read char */
		(void) splx(s);
	} 

	s = spl_psp();
	psp_write_reg(ppsp, unit, W15, W15_EN_DCD | W15_EN_BRK);
	if ((psp_read_reg(ppsp, R0)&R0_DCD) || (pspsoftCAR & UNITMASK(unit) ) )
		tp->t_state |= TS_CARR_ON;
	while ((tp->t_state & TS_CARR_ON) == 0) {
		tp->t_state |= TS_WOPEN;
		sleep((caddr_t) & tp->t_rawq, TTIPRI);
	}
	(void) splx(s);
	DEBUGF(pspdebug & SHOW_REGS, 
	     	printf("psp%d: EXT/STAT IE=%x\n",unit,psp_read_reg(ppsp,R15)));
	if ( error = (*linesw[tp->t_line].l_open)(dev, tp) )
		pspclose( dev );
	DEBUGF(pspdebug & SHOW_OPEN, 
		printf("pspopen%d: error = %d\n", unit, error ));
	return ( error );
}


/*ARGSUSED*/
pspclose(dev)
	dev_t dev;
{
	register int unit = minor(dev);
	register struct tty *tp = &psp[unit];
	register struct pspdevice *ppsp = (struct pspdevice *)tp->t_addr;
	int pspcls();

	DEBUGF(pspdebug & SHOW_OPEN, printf("pspclose%d:\n", unit));

	tp->t_state |= TS_CLOSE;		/* close in progress */
	timeout(pspcls, tp, pspouttime(tp));	/* set timeout for close */

	(*linesw[tp->t_line].l_close)(tp);
	psp_reset_bits(ppsp, unit, W5, W5_BRK); /* reset break */

	if ((tp->t_state&(TS_WOPEN|TS_HUPCLS))||(tp->t_state&TS_ISOPEN)==0){
		/* disable receive and external interrupts */
		psp_reset_bits(ppsp, unit, W1, W1_EN_EI | W1_RXI_ALL); 
		psp_reset_bits(ppsp, unit, W5, W5_RTS);	/* turn rts off */
		*ppsp->ext_reg = (char) 0;		/* dtr off */
		DEBUGF(pspdebug & SHOW_REGS, printf("resetting DTR,RTS "));

	}
	if (tp->t_state & TS_CLOSE)		/* close in progress */
		untimeout(pspcls, tp);		/* cancel the timeout */
	ttyclose(tp);
	DEBUGF(pspdebug & SHOW_REGS, 
            printf("pspclose%d: EXT/STAT IE=%x\n",unit,psp_read_reg(ppsp,R15)));
}

/*
 * return an overestimate of the number of clock ticks it will take
 * to output the remaining output after close has started
 * the multiplcation by 3 and division by 2 give a 50% overestimate 
 * plus hz gives an additional second just in case.
 */

pspouttime(tp)
register struct tty *tp;
{
	return (hz + 3 * hz * tp->t_outq.c_cc / (2 * pspoutputrate[tp->t_ospeed]));
}

/*
 * routine called when close takes longer than it should to flush output
 * and get things going again
 */
pspcls(tp)
register struct tty *tp;
{
	register int s = spl_psp();

	DEBUGF(ttydebug & SHOW_OPEN, printf("pspcls called\n"));
	if (tp->t_state & TS_CLOSE) {		/* if close in progress */
		ttyflush(tp, FREAD|FWRITE);
		tp->t_state &= ~TS_CLOSE;		/* close in progress */
	}
	(void) splx(s);
}


/*ARGSUSED*/
pspread(dev, uio)
	register dev_t dev;
	register struct uio *uio;
{
	register struct tty *tp = &psp[minor(dev)];
	register int l;

	DEBUGF(pspdebug & SHOW_RDWR, printf("In pspread\n"));
	l = (*linesw[tp->t_line].l_read)(tp, uio);
	DEBUGF(pspdebug & SHOW_RDWR, printf("pspread return(%d)\n",l));
	return (l);
}


/*ARGSUSED*/
pspwrite(dev, uio)
	dev_t dev;
	struct uio *uio;
{
	register struct tty *tp = &psp[minor(dev)];

	DEBUGF(pspdebug & SHOW_RDWR, printf("In pspwrite\n"));
	return ((*linesw[tp->t_line].l_write)(tp, uio));
}


/*ARGSUSED*/
pspioctl(dev, cmd, addr, flag)
	dev_t dev;
	caddr_t addr;
{
	register int unit = minor(dev);
	register struct tty *tp = &psp[unit];
	register struct pspdevice *ppsp = (struct pspdevice *)tp->t_addr;
	register int error, s;

	DEBUGF(pspdebug & SHOW_IO, 
		printf("pspioctl%d: cmd=%x, flag=%x\n", unit, cmd, flag));
	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, addr, flag);
#if NAP > 0
	if (cmd == TIOCSETD) 
		if ( *(int *)addr == APLDISC )
			tp->t_oproc = pspoutc;
		else 
			tp->t_oproc = pspstart;
#endif NAP
	if (error >= 0)
		return (error);
	error = ttioctl(tp, cmd, addr, flag);
	if (error >= 0) {
		if (cmd == TIOCSETP || cmd == TIOCSETN || cmd == TIOCLBIS ||
		    cmd == TIOCLBIC || cmd == TIOCLSET ) {
			pspparam(unit);
		}
		return (error);
	}
	s = spl_psp();
	switch (cmd) {
	case TIOCSBRK:
		psp_set_bits(ppsp, unit, W5, W5_BRK);
		DEBUGF(pspdebug, printf("set break"));
		break;
	case TIOCCBRK:
		psp_reset_bits(ppsp, unit, W5, W5_BRK);
		DEBUGF(pspdebug, printf("clr break"));
		break;
	case TIOCSDTR:
		psp_set_bits(ppsp, unit, W5, W5_RTS);
		*ppsp->ext_reg = (char) EXT_DTR;
		DEBUGF(pspdebug, printf("set DTR RTS"));
		break;
	case TIOCCDTR:
		psp_reset_bits(ppsp, unit, W5, W5_RTS);
		*ppsp->ext_reg = (char) 0;
		DEBUGF(pspdebug, printf("clr DTR RTS"));
		break;
	default:
		DEBUGF(pspdebug & SHOW_IO, 
			printf("pspioctl%d: cmd=%x unsupported ", unit, cmd));
		(void) splx(s);
		return (ENOTTY);
	}
	DEBUGF(pspdebug & SHOW_IO, printf(" pspioctl (%d) end\n", unit));
	(void) splx(s);
	return (0);
}


/*
 * Setting the psp parameters (baud rate, data length, etc...)
 */
pspparam(unit)
	register int unit;
{
	register struct tty *tp = &psp[unit];
	register char txbits, mode, rxbits;
	register int s;
	register struct pspdevice *ppsp = (struct pspdevice *)tp->t_addr;

	DEBUGF(pspdebug & SHOW_IO, 
		printf("pspparam%d: tp->t_flags=(%x)\n", unit, tp->t_flags));

	s = spl_psp();
	if (tp->t_ispeed == B0) {
		tp->t_state |= TS_HUPCLS;
		psp_reset_bits(ppsp, unit, W5, W5_RTS); /* turn rts off */
		*ppsp->ext_reg = (char) 0; /* dtr off */
		(void) splx(s);
		return;
	}
	/* Set the baud rate and initial psp settings */
	PSP_SETBAUD(ppsp, unit, tp->t_ispeed);
	if (tp->t_flags & (RAW | LITOUT | PASS8)) {
		rxbits = W3_RX_8BITS | W3_EN_RX;
		txbits = W5_TX_8BITS | W5_EN_TX;
		mode = 0;
	} else {
		rxbits = W3_RX_7BITS | W3_EN_RX;
		txbits = W5_TX_7BITS | W5_EN_TX;
		mode = W4_EN_PAR;
	}
	if (tp->t_flags & EVENP)
		mode |= W4_EVENP;
	if (tp->t_ispeed == B110)
		mode |= W4_STOP2;
	else
		mode |= W4_STOP1;
	mode |= W4_CLOCK16;
	psp_write_reg(ppsp, unit, W4, mode); 	/* set the mode */
	psp_write_reg(ppsp, unit, W3, rxbits);	/* then choose your options */
	psp_write_reg(ppsp, unit, W5, txbits);
	DEBUGF(pspdebug & SHOW_REGS, 
		printf("pspparam%d: rxbits=%x,mode=%x,txbits=%x\n",
			 unit,rxbits,mode,txbits));

	(void) splx(s);
}

/* ARGSUSED */
pspint(ctlr)
	register int ctlr;
{
	register unsigned char port;
	register int didit_a = 0, didit_b = 0;
	register struct pspdevice *ppsp_a = &psp_a;
	register struct pspdevice *ppsp_b = &psp_b;

	DEBUGF(pspdebug & SHOW_INTR, printf("pspint%d\n", ctlr) );
	*ppsp_a->int_ack = (char) 0;       /* interrupt acknowlege */
	while ( (port = psp_read_reg(ppsp_a, R3) ) != 0) {
		DEBUGF(pspdebug & SHOW_INTR, printf("port(R3)=%b\n", port, R3_FMT) );
		if (port & R3_RXIP_A) {
			psprint(ppsp_a, PSP_CHANA);
			didit_a++;
		}
		if (port & R3_RXIP_B) {
			psprint(ppsp_b, PSP_CHANB);
			didit_b++;
		}
		if (port & R3_EXIP_A) {
			pspmint(ppsp_a, PSP_CHANA);
			didit_a++;
		}
		if (port & R3_EXIP_B) {
			pspmint(ppsp_b, PSP_CHANB);
			didit_b++;
		}
		if (port & R3_TXIP_A) {
			pspxint(ppsp_a, PSP_CHANA);
			didit_a++;
		}
		if (port & R3_TXIP_B) {
			pspxint(ppsp_b, PSP_CHANB);
			didit_b++;
		}
		if ( didit_a ) psp_write_reg(ppsp_a,PSP_CHANA,W0,W0_RST_IUS);
		if ( didit_b ) psp_write_reg(ppsp_b,PSP_CHANB,W0,W0_RST_IUS);
		DEBUGF(pspdebug & SHOW_INTR, 
			printf("didit_a=(%d),didit_b=(%d)\n",didit_a,didit_b));
	}
	return ( (didit_a|didit_b) == 0);
}


psprint(ppsp, unit)
	register struct pspdevice *ppsp;
	register int unit;
{
	register int error, charcount = 0;
	struct tty *tp = &psp[unit];
	register char savelsr, c;
	char	exint_pending;

	DEBUGF(pspdebug & SHOW_INTR, printf("psprint%d:", unit));

	exint_pending = (unit ? R3_EXIP_B : R3_EXIP_A);

	while ( (psp_read_reg(ppsp, R0) & R0_DR)  && 
	       ((psp_read_reg((caddr_t)&psp_a, R3) & exint_pending) == 0) ) {

	    	savelsr = psp_read_reg(ppsp, R1);	/* read status */
	    	c = *ppsp->data;		        /* read char */
		DEBUGF(pspdebug & SHOW_INTR, printf(" R1=%b data=%x", savelsr,R1_FMT,c));
		if (savelsr & (R1_PE | R1_OR | R1_FE)){
	   		psp_write_reg(ppsp, unit, W0, W0_RST_ERR);
#if NAP > 0
			if (tp->t_line == APLDISC) {
			    	c = APERROR;
			} else 
#endif
			{  
		    	if ((savelsr & R1_PE) &&
		            (tp->t_flags & (EVENP | ODDP)) != (EVENP|ODDP) &&
			    (tp->t_flags & (EVENP | ODDP)) != 0 ) {
			  	  DEBUGF(pspdebug,
					printf("psp%d: Parity err\n",unit));
					continue;
		   	} 

		    	if ( savelsr & R1_OR ) {
				printf("psp%d: overrun error\n", unit);
				continue;
		    	}

			if ( savelsr & R1_FE ) {
				DEBUGF(pspdebug,
					printf("psp%d: framing error\n",unit));
				continue;
		    	} 
			} /* end else */ 
	    	} /* end if errors */ 
	   	DEBUGF(pspdebug & SHOW_CHARS, printf(" GOT (%c)", c));

		if ( tp->t_line == APLDISC )
			(*linesw[tp->t_line].l_rint)(c, tp);
		else {
			charcount++;
			CPUT( tp->t_cbufp, c, error );
			if ( error ) { 
				printf( "psprint%d: cbuf full\n", unit );
				break;
			}
		} 
	} /* end while */
	if ( charcount ) {
	    TTY_ENQUEUE( &ttyintrq, tp );
	    setsoftTTY();
	}
	DEBUGF(pspdebug & SHOW_INTR, printf(" psprint done\n"));
}


/*
 * Got a transmitter empty interrupt;
 * the psp wants another character.
 */
pspxint(ppsp, unit)
	register struct pspdevice *ppsp;
	register int unit;
{
	char c = (char) unit; 

	DEBUGF(pspdebug & SHOW_INTR, printf("pspxint%d:  ", unit));
	psp_write_reg(ppsp, unit, W0, W0_RST_TXIP); /* reset transmitter int */
	putc(c, &pspxclist);
	setsoftPSP();
	return; 
}
softpsp()
{
	register struct tty *tp;
	register int unit;
   	register int s = spl5();	
	register struct pspdevice *ppsp;
 
	unit = getc(&pspxclist); 
	(void) splx(s);
	s = spl_psp();
	tp = &psp[unit]; 
	ppsp = (struct pspdevice *)tp->t_addr;
 
#if NAP > 0
	if (tp->t_line == APLDISC) {
		DEBUGF(pspdebug& SHOW_INTR, printf("calling AP xint \n"));
		(*linesw[tp->t_line].l_start)(tp);
	} else 
#endif
	{
		tp->t_state &= ~TS_BUSY;
		if (tp->t_outq.c_cc <= 0) {
			if (psp_read_reg(ppsp, R1) & R1_ALL_SENT) {
				DEBUGF(pspdebug & (SHOW_INTR|SHOW_REGS), 
					printf("disabling transmitter ints\n"));
				psp_reset_bits(ppsp, unit, W1, W1_EN_TXI); 
			}
		}
		if (tp->t_line)
			(*linesw[tp->t_line].l_start)(tp);
		else
			pspstart(tp);
	}
	DEBUGF(pspdebug & SHOW_INTR, printf("pspxint end\n"));
	(void) splx(s);
}

pspstart(tp)
	register struct tty *tp;
{
	register int c, s;
	register int unit = minor(tp->t_dev);
	register struct pspdevice *ppsp = (struct pspdevice *)tp->t_addr;

	DEBUGF(pspdebug & SHOW_IO, printf("pspstart%d:", unit));
	s = spl_psp();
	if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) {
		DEBUGF(pspdebug & SHOW_IO, printf("TIMEOUT|BUSY|STOP\n"));
		splx(s);
		return;
	}
	while (psp_read_reg(ppsp, R0) & R0_TXE) {
		if ((tp->t_outq.c_cc <= 0) || (c = getc(&tp->t_outq)) == -1) {
			goto out1;		/* nothing to output */
		}
		DEBUGF(pspdebug & SHOW_CHARS, printf(" PUT %x (%c)", c, (char)(c)));
		if ((tp->t_flags & (RAW | LITOUT)) || (c <= 0177)) {
			*ppsp->data = (char) c; 
		} else {
			timeout(ttrstrt, (caddr_t)tp, (c & 0177));
			tp->t_state |= TS_TIMEOUT;
			DEBUGF(pspdebug & SHOW_CHARS, 
				printf("pspstart%d: timeout\n",unit));
			goto out1;
		}
	}
	tp->t_state |= TS_BUSY;
	DEBUGF(pspdebug & (SHOW_IO|SHOW_REGS), printf(" enable TX int"));
	psp_set_bits(ppsp, unit, W1, W1_EN_TXI);
	goto out2;
out1:
	DEBUGF(pspdebug & SHOW_IO, printf(" out1"));
	if (psp_read_reg(ppsp, R1) & (char) R1_ALL_SENT) {
		DEBUGF(pspdebug & (SHOW_IO|SHOW_REGS), 
			printf(" disable TX"));
		psp_reset_bits(ppsp, unit, W1, W1_EN_TXI);
		tp->t_state &= ~TS_BUSY;
	}
	if (tp->t_outq.c_cc <= TTLOWAT(tp)) {
		if (tp->t_state & TS_ASLEEP) {
			tp->t_state &= ~TS_ASLEEP;
			wakeup((caddr_t) & tp->t_outq);
		}
		if (tp->t_wsel) {
			selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
			tp->t_wsel = 0;
			tp->t_state &= ~TS_WCOLL;
		}
	}
out2:
	(void) splx(s);
	DEBUGF(pspdebug & SHOW_IO, printf(" out2\n"));
}


/*  
 *  When an external interrupt is generated, all of the interrupts are
 *  latched.  Break is the only one which generates an interrupt for 
 *  both the high-to-low and low-to-high transition.   This means it
 *  is necessary to save a copy of the old msr in memory, then read
 *  the current one and see which bits have changed.  The only
 *  interrupts enabled are BRK and DCD so we must check for both here.
 */
pspmint(ppsp, unit)
	register struct pspdevice *ppsp;
	register int unit;
{
	register int error, c;
	register struct tty *tp = &psp[unit];
	register char newmsr, msrdiff;

	newmsr = psp_read_reg(ppsp, R0);
	msrdiff = pspsavemsr ^ newmsr;       /* interrupts which have changed */
	DEBUGF(pspdebug & SHOW_INTR, 
		printf("pspmint%d: msr=%b msrdiff=%b\n", unit, newmsr, R0_FMT,
			msrdiff, R0_FMT));

	if (msrdiff & R0_BRK & pspsavemsr ) {	/* 1 -> 0 transition */
						/* end of a break */
		c = *ppsp->data;  		/* read extra null char */
#if NAP > 0 
		if (tp->t_line == APLDISC) {
			c = APBREAK;
			DEBUGF(pspdebug & SHOW_CHARS, 
				printf("AP GOT -> (%x)\n", (char)c));
			(*linesw[tp->t_line].l_rint)(c, tp);
		} else
#endif NAP
		{
			if ( (tp->t_flags & RAW) == 0 ) /* if not raw mode, */
				c = tp->t_intrc;     /* send interrupt char */
	    		/* else send the null char read earlier */

			CPUT( tp->t_cbufp, c, error );
		   	if ( error ) printf( "pspmint%d: cbuf full\n", unit );
			TTY_ENQUEUE( &ttyintrq, tp );
			setsoftTTY();
		}
	} 

	if ( msrdiff & R0_DCD ) {
	    	if ( newmsr & R0_DCD ) { 
			DEBUGF(pspdebug & SHOW_REGS,printf("Carrier detect "));
			if ((tp->t_state & TS_CARR_ON) == 0) {
				tp->t_state |= TS_CARR_ON;
				wakeup((caddr_t) & tp->t_rawq);
			}
	    	} else { 
			DEBUGF(pspdebug & SHOW_REGS, printf("carrier lost "));
			if (tp->t_state & TS_CARR_ON) {
				gsignal(tp->t_pgrp, SIGHUP);
				gsignal(tp->t_pgrp, SIGCONT);
				ttyflush(tp, FREAD | FWRITE);
			}
			tp->t_state &= ~TS_CARR_ON;
	    	}
	}
	
	pspsavemsr = newmsr; /* store away the newmsr for next interrupt cycle */
	psp_write_reg(ppsp, unit, W0, W0_RST_EI);
	DEBUGF(pspdebug & SHOW_REGS, printf("pspsavemsr=%x\n", pspsavemsr));
}


/*
 * The following routines handle the reading and writing to the 8530 chip
 */
char psp_read_reg(ppsp, reg)
	register struct pspdevice *ppsp;
	register int reg;
{
	char c;

	*ppsp->control = (char) (reg & REGMASK);
	c = *ppsp->control;
	return ( c );
}


psp_write_reg(ppsp, unit, reg, data)
	register struct pspdevice *ppsp;
	register int unit;
	register int reg;
	char data;
{
	*ppsp->control = (char) (reg & REGMASK);
	psp_write_regs[unit][reg] = data;
	*ppsp->control = data;
}


psp_set_bits(ppsp, unit, reg, bits)
	register struct pspdevice *ppsp;
	register int unit;
	register int reg;
	char bits;
{
	*ppsp->control = (char) (reg & REGMASK);
	psp_write_regs[unit][reg] |= bits;
        *ppsp->control = psp_write_regs[unit][reg];
}


psp_reset_bits(ppsp, unit, reg, bits)
	register struct pspdevice *ppsp;
	register int unit;
	register int reg;
	char bits;
{
	*ppsp->control = (char) (reg & REGMASK);
	psp_write_regs[unit][reg] &= ~bits;
	*ppsp->control = psp_write_regs[unit][reg];
}

pspselect(dev, rw)
	dev_t dev;
	int rw;
{
	register struct tty *tp = &psp[minor(dev)];

	return ((*linesw[tp->t_line].l_select)(dev, rw));
}


#if NAP > 0
/*
 * This routine is for the support of the 3812 printer. It will perform
 * one of three functions:	
 *		1) set the break bit 
 *		2) clear the break bit
 *	    or	3) output one character.
 */
pspoutc(tp, c)
	register struct tty *tp;
	register int c;
{
	register int unit = minor(tp->t_dev);
	register struct pspdevice *ppsp = (struct pspdevice *)tp->t_addr;
	int s = spl_psp();

	DEBUGF(pspdebug & SHOW_CHARS, printf("pspoutc: PUT (%x)\n", c));
	if (c >= 0)
		*ppsp->data = c;
	else {
		switch (c) {
		case APBREAKON:
			psp_set_bits(ppsp, unit, W5, W5_BRK);
			break;
		case APBREAKOFF:
			psp_reset_bits(ppsp, unit, W5, W5_BRK);
			break;
		case APINTMASK:
			psp_set_bits(ppsp, unit, W1, W1_EN_EI | W1_EN_TXI);
			break;
		}
	}
	(void) splx(s);
}
#endif NAP

pspreset ()
{
	register struct pspdevice *ppsp;
	register struct tty *tp;
	register int port;
	register int s, x;

	x = IN(CRRB);
	x &= RESET8530;		/* reset is forced active */
	OUT(CRRB, x);

	x = IN(CRRB);
	x |= RELEA8530;		/* reset is forced inactive */
	OUT(CRRB, x);

	psp_write_reg( &psp_a, PSP_CHANA, W9, W9_RST_HARD);
	for (port = 0, tp = &psp[port] ; port < NPORTS; port++, tp++) {
		ppsp = (port) ? &psp_b : &psp_a;
		psp_write_reg(ppsp, port, W0, W0_RST_IUS);
		psp_write_reg(ppsp, port, W11,W11_SCLK); /* Clock register */
		if (tp->t_state & TS_ISOPEN) {	/* reset if already opened */
			pspparam(port);
			ppsp = (struct pspdevice *)tp->t_addr;
			s = spl_psp();
			psp_set_bits(ppsp,port,W5,W5_RTS); /* RTS (on chip) */
			*ppsp->ext_reg = EXT_DTR; /* DTR on */
			psp_set_bits(ppsp,port,W1,W1_RXI_ALL|W1_EN_EI);
			(void) splx(s);
			psp_write_reg(ppsp,port,W15,W15_EN_DCD|W15_EN_BRK);
		}
		psp_write_reg(ppsp, port, W9, W9_EN_MI);
	}
}

#endif NPSP
