/*----------------------------------------------------------------------*/
/*	(C) 1992 - Cyclades Corporation.				*/
/*----------------------------------------------------------------------*/
/*	This file : cyys.c						*/ 
/*									*/ 
/*	Contains code fragments of Cyclom-Y Unix Device Driver.		*/
/*	This file is provided only as an example and contains proprie	*/
/*	tary code from Cyclades Corporation.				*/
/*									*/
/*----------------------------------------------------------------------*/


/* all Cyclom-y global variable names begin with "gys_" */

ushort	gys_n_chip[MAX_BOARD];				/* # chips found.    */
ushort	gys_n_chan[MAX_BOARD];				/* # channels found. */
uchar	gys_present[MAX_BOARD] = { FLAG_OFF,FLAG_OFF,	/* flag card present */
				   FLAG_OFF,FLAG_OFF };

uchar		gys_baud_cor[] = {		/* 25 MHz baud rate table */
			0x00,0x04,0x04,0x04,0x04,0x04,0x03,0x03,0x03,0x02,
			0x02,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00
		};
uchar		gys_baud_bpr[] = {		/* 25 MHz baud rate table */
			/*
			speeds:
			0000,0050,0075,0110,0134,0150,0200,0300,0600,1200,
			1800,2400,4800,9600,19.2,38.2,57.6,76.8,1152,1500
			*/
			0x00,0xf5,0xa3,0x6f,0x5c,0x51,0xf5,0xa3,0x51,0xa3,
			0x6d,0x51,0xa3,0x51,0xa3,0x51,0x36,0x29,0x1b,0x15

		};
	
uchar	*gys_pt_chip[MAX_BOARD*MAX_CHIP_BOARD];		/* pointers to chip  */


(...)


/*-----------------------------------------------------------------------
	Routine :	cyysinit()
	Parameters:	none.
	Description :	UNIX init (it is called at UNIX boot time).
	Return:
	Observations:
-----------------------------------------------------------------------*/

cyysinit()
{
ushort	i,j;
uchar	x;

	/* for each board, initializes io,irq global variables */

	for ( i = 0 ; i < MAX_BOARD ; i++ ) {
		/* initializes global variables */

		(...)
		
		/* set pointer to the chips */

		for ( j = 0 ; j < MAX_CHIP_BOARD ; j++ ) {
			gys_pt_chip[i * MAX_CHIP_BOARD + j] =
				gys_address[i] + ( j * CHIP_ADDRESS_SPACE );
		}

		/* initializes channels variables */

		(...)
		
		/* check board presence */

		if ( (gys_n_chip[i] = rys_presence(gys_address[i])) != 0 ) { 
			gys_n_chan[i] = gys_n_chip[i] * 4;
			gys_present[i] = FLAG_ON;
			printcfg("Cyclom",( (uint) (gys_user_addr[i]) ) >> 4,
				(uint)0x200,gys_irq[i],-1,
				"%s #%d %s - %d channels.",MYNAME,i+1,VERSION,
				gys_n_chan[i]);
   			/* clear interrupts */
   			x = *(gys_pt_chip[i*MAX_CHIP_BOARD] + CLEAR_INTR);
		} else {
			gys_present[i] = FLAG_OFF;
			gys_irq[i] = 0;
			printf("\nWarning: %s configured at 0x%x not found.\n\n"
				,MYNAME,gys_user_addr[i]);
		}
	   }
}



/*-----------------------------------------------------------------------
	Routine :	cyysintr(vec)
	Parameters:	vec -	interrupt vector.
	Description :	Called at an Cyclom-y interrupt.
	Return:
	Observations:	Do not access any Channel Register (only global and
			virtual) before end of service.
-----------------------------------------------------------------------*/

cyysintr(vec)

int	vec;
{
uchar			x,status,mdm_status,mdm_change;
uchar			*pt_chip,*prbuf;
ushort			i,board,chip,channel,udev;


   /* check the board number comparing the vector */

   (...) (set board variable)

   if (i == MAX_BOARD)	 return;		/* spurious interrupt */

   /* loop polling all chips in the board */

   for ( chip = 0 ; chip < gys_n_chip[board] ; chip ++) {
     pt_chip = gys_pt_chip[board * MAX_CHIP_BOARD + chip];
     while ( (status = *(pt_chip + 2*SVRR)) != 0x00) {
	if (status & 0x01) {			/* reception interrupt */
		(...)
		
		Reception processing (see lib_y.c)
		
		(...)
	}

	if (status & 0x02) {			/* transmission interrupt */
		(...)
		
		Transmission processing
		
		(...)
	}

	if (status & 0x04) {			/* modem interrupt */
		(...)
		
		Modem processing
		
		(...)
	}
     }		/* end while status != 0 */
   }		/* end loop for chip... */

   /* clear interrupts */
   x = *(gys_pt_chip[board*MAX_CHIP_BOARD] + CLEAR_INTR);

}


/*----------------------------------------------------------------------*
 *	Auxialiary Routines						*
 *----------------------------------------------------------------------*/


/*-----------------------------------------------------------------------
	Routine :	rys_param(udev,cmd)
	Parameters:	udev,cmd.
	Description :	if cmd == TURNON, programs CD1400 corresponding to
				  udev according tp structure.
			if cmd == TURNOFF, disable CD1400 reception and
				  disable interrupts. 
			if cmd == RX_ENABLE, enable rx interrupts.
	Return:		0 if ok, -1 otherwise.
	Observations:	
-----------------------------------------------------------------------*/

ushort	rys_param(udev,cmd)

ushort	udev,cmd;
{
uchar			*pt_chip;
ushort			speed,cflag,iflag,x;
uchar			cor1,cor2,cor3,cor4,cor5;
ushort			channel;


	/* pt_chip and channel */

	pt_chip = gys_pt_chip[udev/4];
	channel = udev % 4;
	tp = &cyys_tty[udev];

	(...)

	switch(cmd) {
	   case TURNON:

		(...)
		
		(set cor1, cor2, cor3, cor4, cor5  according configuration)

		*(pt_chip + 2*SCHR1) = gys_xonxoff[udev].ss_start;
		*(pt_chip + 2*SCHR2) = gys_xonxoff[udev].ss_stop;
		*(pt_chip + 2*COR1) = cor1;
		*(pt_chip + 2*COR2) = cor2;
		*(pt_chip + 2*COR3) = cor3;
		*(pt_chip + 2*COR4) = cor4;
		*(pt_chip + 2*COR5) = cor5;

		rys_wr_cmd(pt_chip,0x4e);		/* COR123 changed */

		*(pt_chip + 2*MCOR1) = 0x1c;		/* CD intr, DTR */
		*(pt_chip + 2*MCOR2) = 0x10;		/* CD intr */
		*(pt_chip + 2*RTPR) = 0x20;		/* 32ms rx timeout */

		if (cflag & CLOCAL) {
			gys_rxintr[udev] = 0x10;	/* without modem intr */
		} else {
			gys_rxintr[udev] = 0x90;	/* with modem intr */
		}

		(...)

		rys_wr_cmd(pt_chip,0x1a);		/* enable channel */
		splx(x);
		break;

	   case TURNOFF:
		x = spl_ys();
		*(pt_chip+2*CAR)=(uchar)(channel&0x0003);   /* index channel */
			
		rys_wr_cmd(pt_chip,0x15);		/* disable channel */
		splx(x);
		break;
	
	   case RX_ENABLE:
		x = spl_ys();
		*(pt_chip + 2*CAR) = (uchar)(channel&0x0003);
		*(pt_chip + 2*SRER) = gys_rxintr[udev];
		splx(x);
	}
	return(0);
}


/*-----------------------------------------------------------------------
	Routine :	rys_modem(udev,cmd)
	Parameters:	udev, cmd .
	Description :	if cmd = TURNON, turn on DTR.
			if cmd = TURNOFF, turn off DTR.
			if cmd = NOCMD, nothing.
	Return:		0 if no carrier, not 0 if there is carrier.
	Observations:
-----------------------------------------------------------------------*/
uchar rys_modem(udev,cmd)

ushort	udev,cmd;

{
uchar	*pt_chip;
ushort	x;
uchar	cd_state;

	pt_chip = gys_pt_chip[udev/4];

	x = spl_ys();
	*(pt_chip + 2*CAR) = (uchar)(udev & 0x03);

	switch(cmd) {
		case TURNON:
			*(pt_chip + 2*MSVR2) = 0x11;
			/* enable only modem */
			*(pt_chip + 2*SRER) = (gys_rxintr[udev] & 0x80);
			break;

		case TURNOFF:
			*(pt_chip + 2*MSVR1) = 0x00;
			*(pt_chip + 2*MSVR2) = 0x00;
			break;

		case NOCMD:
			break;
	}
	cd_state = *(pt_chip + 2*MSVR1) & 0x10;
	splx(x);

	return(cd_state);
}



/*-----------------------------------------------------------------------
	Routine :	rys_presence(address)
	Parameters:	address.
	Description :	Check presence of a board at address.
			Reset the serial contollers. Performs global function
			Initialization.
	Return:		number of cirrus controller detected ( 1, 2 or 4 ),
			0 if board not found.
	Observations:	
-----------------------------------------------------------------------*/
#define AT_100us	20000
#define AT_500us	60000

ushort	rys_presence(address)

uchar	*address;
{
uchar 	*pt_chip;
ushort	i,j;
ushort	x;

	/* reset Cyclom-16Y (Cyclom-8Y doesn't care) */

	x = (address + RESET_16);	/* asserts reset 16 */
	x = (address + CLEAR_INTR);	/* release reset */

	/* delay after reset */
	
	for (i=0 ; i < AT_500us ; i++) {
		j++;
	}
	
	for ( i = 0 ; i < MAX_CHIP_BOARD ; i++ ) {
		pt_chip = address + (i * CHIP_ADDRESS_SPACE);

		/* wait for cirrus startup */

		for ( j = 0 ; j < AT_100us ; j++ ) {
			if ( *(pt_chip + 2*CCR) == 0x00 ) {
				break;
			}
		}
		if ( j == AT_100us )	return (i);
	
		/* reset GFRCR register */

		*(pt_chip + 2*GFRCR) = 0;

		/* issue a reset command */

		*(pt_chip + 2*CCR) = CMD_RESET;

		/* wait for cirrus initialization */

		for ( j = 0 ; j < AT_500us ; j++ ) {
		   /* cyrrus supported versions : 0x40 to 0x4f */
		   if ( (*(pt_chip + 2*GFRCR) & 0xf0) == 0x40 )  {
				break;
		   }
		}
		if ( j == AT_500us )	return (i);

		/* initialize GCR setting channe 1 to serial */

		*(pt_chip + 2*GCR) = CH0_IS_SERIAL;

		/* set PPR to generate 1 ms clock to internal timer */

		*(pt_chip + 2*PPR) = CLOCK_25_1MS;

	}
	return(i);
}



/*-----------------------------------------------------------------------
	Routine :	rys_wr_cmd(pt_chip,cmd)
	Parameters:	pt_chip - address of the chip.
			cmd	- command to be issued.
	Description :	Send cmd to channel.
	Return:		0  if could send cmd, -1 otherwise.
	Observations:	This routine assumes that the correct set of
			registers are already selected.
-----------------------------------------------------------------------*/
#define	CMD_TIMEOUT	2000

ushort	rys_wr_cmd(pt_chip,cmd)

uchar	*pt_chip;
uchar	cmd;
{
ushort	i,x;

	x = spl_ys();
	for ( i = 0 ; i < CMD_TIMEOUT ; i++ ) {
		if ( *(pt_chip + 2*CCR) == 0 )	break;
	}
	if ( i == CMD_TIMEOUT ) {
		splx(x);
		return (-1);
	}
	*(pt_chip + 2*CCR) = cmd;
	splx(x);
	return(0);
}



