/*********************************************************************** 
* NOTICE
* All files contained on this disk are subject to the licensing conditions
* issued by MOTOROLA Inc.
*
* All files are copyright 1993 by MOTOROLA Inc. 
************************************************************************/


/********************************************************
 * File:	scc.c
 *
 * Description:
 *	This file contains routines used by SCC controllers
 *	to initialize an SCC channel
 *	irrespective of their mode of operation.
 *
 * Routines:
 *	scc_init
 *	scc_pool
 *	scc_irx
 *	scc_itx
 *	flush_queues
 *	scc_regs
 *
 * Author:
 *	Jonathan Masel
 ********************************************************/

#include "gct.h"
#include "config.h"
#include "msg.h"
#include "quicc.h"
#include "types.h"
#include "mtypes.h"
#include "bss.h"
#include "status.h"
#include "modules.h"
#include "fatal.h"
#include "registers.h"


extern void issue_cmd(unsigned short cmd, volatile QUICC *quicc);

/********************************************************
 * routine:	scc_init
 *
 * description:
 *	Initialize the global SCC parameters.
 *	These are parameters of relevance to
 *	all SCC's, irrespective of their mode
 *	of operation.
 *
 * arguments:
 *	port	the number of the SCC port (0, 1 or 2)
 *	dr	points to the driver configuration structure
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
scc_init(port, scc)
int port;
struct scc *scc;
{
	GCT *gct;
	CHILD *child;
	WORK_AREA *wa;
	QUICC *quicc;

	GETGCT(gct);
	child = &gct->driver->child[scc->child_id];
	child->key = scc->child_id;
	child->flags = 0;
	wa = (WORK_AREA *)child->work_area;
	quicc = scc->quicc;

	/*
	 * initialize this child's work area
	 */
	wa->channel = port;
	wa->rxerr_mask = scc->rxerr_mask;
	wa->minpool = scc->minpool;
	wa->rlen = scc->mrblr;
	wa->tsize = scc->tsize;
	wa->rsize = scc->rsize;
	wa->rxind_event = (RX_IND << 16) | scc->child_id;
	wa->txconf_event = (TX_CONF << 16) | scc->child_id;
	wa->txe_event = (TX_ERR << 16) | scc->child_id;
	wa->busy_event = (BUSY << 16) | scc->child_id;
	wa->intr_trace = scc->intr_trace;

	wa->quicc = quicc;
	wa->pram = &quicc->pram[port].scc;
	wa->enet_pram = &quicc->pram[port].enet_scc;
	wa->regs = &quicc->scc_regs[port];

	/*
	 * see if loop back mode if requested
	 */
	if( (scc->loop_mode >= 0) &&
		(scc->loop_mode < gct->driver->max) ){
		child->flags |= LOOP_MODE;
		child->lower = scc->loop_mode;
	}

	/*
	 * disable transmit/receive
	 */
	wa->regs->scc_gsmra = 0;
	wa->regs->scc_sccm = 0;
	flush_queues(scc->child_id);


	/*
	 * first set scc common protocol parameters
	 * (same for HDLC UART and BiSYNC)
	 *	rx BD FC
	 *	tx BD FC
	 *	maximum rx buffer len
	 */

	wa->pram->pscc.h.rbase = scc->rbase;
	wa->pram->pscc.h.tbase = scc->tbase;
	wa->pram->pscc.h.rfcr = scc->rfcr;
	wa->pram->pscc.h.tfcr = scc->tfcr;
	wa->pram->pscc.h.mrblr = scc->mrblr;
	wa->pram->pscc.h.rbptr = scc->rbptr;
	wa->pram->pscc.h.tbptr = scc->tbptr;

	/*
	 * Set Transparetn bits in the childs flags.
	 */
	if( scc->scc_gsmrb & TTX ) {
		wa->more_flags |= SCC_TTX;
		wa->pram->pscc.t.crc_p = scc->crc_p;
	}
	if( scc->scc_gsmrb & TRX ) {
		wa->more_flags |= SCC_TRX;
		wa->pram->pscc.t.crc_c = scc->crc_c;
	}


	/*
	 * then "empty" the 68360 Tx and Rx BD tables
	 */
	RBD_ADDR(wa)[0].status = 0;
	TBD_ADDR(wa)[0].status = 0;

}



/********************************************************
 * routine:	scc_pool
 *
 * description:
 *	Build a pool of receive buffers.
 *	All buffers previously allocated to the receive
 *	pool of this port have been released, a new receive
 *	buffer pool is allocated here.
 *	Note that an extra R_FRAME is allocated at the
 *	end of the pool. This guarantees that the linked list
 *	of R_FRAMES comprising the rx-queue/rx-pool is
 *	never empty (an R_FRAME in the linked list is never
 *	linked to the receive BD table if its nextb pointer
 *	is null).	
 *
 * arguments:
 *	n		the driver's child number
 *	num		number of buffers (not including extra
 *			R_FRAME at end of pool)
 *
 * return code:
 *	0		on success
 *	non-zero if not enough buffers were available
 *
 * side effects:
 *
 ********************************************************/
scc_pool(n, num)
unsigned short n;
int num;
{
	int i;
	unsigned short class;
	GCT *gct;
	struct work_area *wa;
	R_FRAME *r, *r1;
	extern R_FRAME *getr();

	GETGCT(gct);
	wa = (struct work_area *)gct->driver->child[n].work_area;

	/*
	 * determine class, then
	 * allocate new buffers
	 */
	if( wa->channel == 0 )
		class = RX | BUFFER | SCC0;
	else if( wa->channel == 1 )
		class = RX | BUFFER | SCC1;
	else if( wa->channel == 2 )
		class = RX | BUFFER | SCC2;
	else if( wa->channel == 3 )
		class = RX | BUFFER | SCC3;

	/*
	 * loop until <= num to add the
	 * "dummy" R_FRAME at the end of the pool.
	 */
    if( num > 0 ){
	r1 = 0;
	for(i = 0; i <= num; i++){
		r = getr(class, n);

		/*
		 * getr failed - a pool is created
		 * but of smaller size
		 */
		if( !r )
			break;

		/*
		 * rx_q is first R_FRAME allocated
		 */
		if( !wa->rx_q ){
			wa->rx_q = wa->pool_q = r;
		}
		if( r1 )
			r1->nextb = r;

		/*
		 * hdr.id always contains the driver child number
		 */
		r->hdr.id = n;
		r1 = r;
		wa->pool_cnt++;
	}

	/*
	 * r1 is the last in the pool
	 */
	r1->nextb = 0;
	wa->rtp_q = r1;
    }

	wa->quicc_rbd = RBD_ADDR(wa);
	wa->quicc_pbd = RBD_ADDR(wa);
	wa->rfree = wa->rsize;		/* all BDs in table are free */

	/*
	 * prepare the receive table
	 */
	scc_irx(n, wa->rsize);
	if( num > 0 )
		fill_rtab(wa);
	return(i<num);
}


/********************************************************
 * routine:	scc_irx
 *
 * description:
 *	Intialize the SCC receive BD table.
 *	All status bits (except Wrap on the last BD)
 *	are cleared (the Empty bit will be set later
 *	for each BD by fill_rtab).
 *
 * arguments:
 *	n		the driver's child number
 *	rsize		the number of BD's in the table
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
scc_irx(n, rsize)
unsigned short n;
int rsize;
{
	GCT *gct;
	WORK_AREA *wa;
	CHILD *child;
	int i;
	register QUICC_BD *b;
	unsigned char *s;

	GETGCT(gct);
	child = &gct->driver->child[n];
	wa = (struct work_area *)child->work_area;

	wa->rfree = wa->rsize;
	wa->quicc_rbd = RBD_ADDR(wa);
	wa->quicc_pbd = RBD_ADDR(wa);
	wa->rcount = 0;
	wa->rerr_cnt = 0;

	for(i = 0; i < rsize; i++){
		(RBD_ADDR(wa) + i)->status = 0;
	}
	(RBD_ADDR(wa) + (rsize-1))->status = R_W;

	/*
	 * if the uart is to operate in character mode,
	 * construct the rx BD table using dummy (1 byte) buffers
	 */
	if( child->flags & UART_CHARMODE ){
		b = RBD_ADDR(wa);
		s = (unsigned char *)&wa->conf_q;
		for(i = 0; i < rsize; i++){
			b->buf = s++;
			b->length = 1;
			b->status = wa->BDrx | R_E;
			b++;
		}
		(RBD_ADDR(wa) + (rsize-1))->status |= R_W;
	}
}



/********************************************************
 * routine:	scc_itx
 *
 * description:
 *	Initialize an SCC transmit table.
 *
 * arguments:
 *	n		the driver's child number
 *	tsize		the size of the tx table
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
scc_itx(n, tsize)
{
	GCT *gct;
	CHILD *child;
	WORK_AREA *wa;
	int i;
	T_FRAME *t;
	register QUICC_BD *b;
	unsigned char *s;

	GETGCT(gct);
	child = &gct->driver->child[n];
	wa = (struct work_area *)child->work_area;

	/*
	 * release all existing T_FRAMEs
	 */
	t = wa->conf_q;
	while( t ){
		t->hdr.status = TX_FAIL;
 		conf_frm(t, gct->driver->child[n].upper);
		t = t->layer[LAYER(DRIVER)].nextf;
	}
	wa->conf_q = 0;
	wa->tx_q = 0;
	wa->req_q = 0;

	wa->tfree = wa->tsize;
	wa->quicc_tbd = TBD_ADDR(wa);
	wa->quicc_cbd = TBD_ADDR(wa);
	wa->tcount = 0;
	wa->terr_cnt = 0;

	for(i = 0; i < tsize; i++){
		(TBD_ADDR(wa) + i)->status = 0;
	}
	(TBD_ADDR(wa) + (tsize-1))->status |= T_W;

	/*
	 * if the uart is to operate in character mode,
	 * construct the tx BD table using dummy (1 byte) buffers
	 */
	if( child->flags & UART_CHARMODE ){
		b = TBD_ADDR(wa);
		s = (unsigned char *)&wa->req_q;
		for(i = 0; i < tsize; i++){
			b->buf = s++;
			b->length = 1;
			b++;
		}
	}
}

/********************************************************
 * routine:	flush_queues
 *
 * description:
 *	Flush all of an SCC's queues.
 *
 * arguments:
 *	n		the driver's child number
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
flush_queues(n)
int n;
{
	register WORK_AREA *wa;
	CHILD *child;
	GCT *gct;
	register T_FRAME *t, *t1;

	GETGCT(gct);
	child = &gct->driver->child[n];
	wa = (WORK_AREA *)child->work_area;

	/*
	 * free T_FRAMES (starting at conf_q)
	 */
	t = wa->conf_q;
	while( t ){
		t1 = t->layer[LAYER(DRIVER)].nextf;
		conf_frm(t, child->upper);
		t = t1;
	}
	wa->conf_q = wa->tx_q = wa->req_q = 0;

	/*
	 * release R_FRAMEs in receive pool
	 * (relm will advance along r->nextb)
	 */
	if( wa->rx_q ){
		wa->rx_q->flags &= ~RX_POOL;
		relm((HDR *)wa->rx_q);
	}
	wa->rx_q = wa->pool_q = wa->rtp_q = 0;
	wa->pool_cnt = 0;
}

/********************************************************
 * routine:	scc_regs
 *
 * description:
 *	Set up all SCC registers from the configuration strcuture.
 *
 * arguments:
 *	i		the driver's child number
 *	scc		points to the configuration structure
 *
 * return code:
 *	0		always succeeds
 *
 * side effects:
 *
 ********************************************************/
scc_regs(n, scc)
int n;
struct scc *scc;
{
	GCT *gct;
	WORK_AREA *wa;
	volatile QUICC *quicc;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[n].work_area;
	quicc = scc->quicc;

	issue_cmd(INIT_RXTX_PARAMS | (wa->channel << 6), quicc);

	wa->regs->scc_gsmra = scc->scc_gsmra;
	wa->regs->scc_gsmrb = scc->scc_gsmrb;
	wa->regs->scc_psmr = scc->scc_psmr;
	wa->regs->scc_dsr = scc->scc_dsr;
	wa->regs->scc_sccm= scc->scc_sccm;
}
