/*********************************************************************** 
* 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:	smc.c
 *
 * Description:
 *	This file contains routines used by SMC controllers
 *	to initialize an SMC channel
 *	irrespective of their mode of operation.
 *
 * Routines:
 *	smc_init
 *	smc_pool
 *	smc_irx
 *	smc_itx
 *	smc_regs
 *
 * Author:
 *	Ofrit Lesser
 ********************************************************/

#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:	smc_init
 *
 * description:
 *	Initialize the global SMC parameters.
 *	These are parameters of relevance to
 *	all SMC's, irrespective of their mode
 *	of operation.
 *
 * arguments:
 *	port	the number of the SMC port (0, 1)
 *	smc	points to the driver configuration structure
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
smc_init(port, smc)
int port;
struct smc *smc;
{
	GCT *gct;
	CHILD *child;
	WORK_AREA *wa;
	QUICC *quicc;
	struct smc_regs	*regs;

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

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

	wa->quicc = quicc;
	wa->pram = &quicc->pram[port + 2].scc;
	wa->regs = (struct scc_regs *)&quicc->smc_regs[port];

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

	/*
	 * disable transmit/receive
	 */
	regs = (struct smc_regs *)wa->regs;
	regs->smc_smcmr = 0;
	regs->smc_smcm = 0;
	flush_queues(smc->child_id);


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

	wa->pram->pothers.idma_smc.psmc.u.rbase = smc->rbase;
	wa->pram->pothers.idma_smc.psmc.u.tbase = smc->tbase;
	wa->pram->pothers.idma_smc.psmc.u.rfcr = smc->rfcr;
	wa->pram->pothers.idma_smc.psmc.u.tfcr = smc->tfcr;
	wa->pram->pothers.idma_smc.psmc.u.mrblr = smc->mrblr;
	wa->pram->pothers.idma_smc.psmc.u.rbptr = smc->rbptr;
	wa->pram->pothers.idma_smc.psmc.u.tbptr = smc->tbptr;


	/*
	 * then "empty" the QUICC Tx and Rx BD tables
	 */
	SMC_RBD_ADDR(wa)[0].status = 0;
	SMC_TBD_ADDR(wa)[0].status = 0;
}



/********************************************************
 * routine:	smc_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:
 *
 ********************************************************/
smc_pool(n, num)
unsigned short n;
int num;
{
	int i;
	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;

	/*
	 * 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((unsigned short)R_BUFG, 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 = SMC_RBD_ADDR(wa);
	wa->quicc_pbd = SMC_RBD_ADDR(wa);
	wa->rfree = wa->rsize;		/* all BDs in table are free */

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


/********************************************************
 * routine:	smc_irx
 *
 * description:
 *	Intialize the SMC 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 smc_fill_rtab).
 *
 * arguments:
 *	n		the driver's child number
 *	rsize		the number of BD's in the table
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
smc_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 = SMC_RBD_ADDR(wa);
	wa->quicc_pbd = SMC_RBD_ADDR(wa);
	wa->rcount = 0;
	wa->rerr_cnt = 0;

	for(i = 0; i < rsize; i++){
		(SMC_RBD_ADDR(wa) + i)->status = 0;
	}
	(SMC_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 = SMC_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++;
		}
		(SMC_RBD_ADDR(wa) + (rsize-1))->status |= R_W;
	}
}



/********************************************************
 * routine:	smc_itx
 *
 * description:
 *	Initialize an SMC transmit table.
 *
 * arguments:
 *	n		the driver's child number
 *	tsize		the size of the tx table
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
smc_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 = SMC_TBD_ADDR(wa);
	wa->quicc_cbd = SMC_TBD_ADDR(wa);
	wa->tcount = 0;
	wa->terr_cnt = 0;

	for(i = 0; i < tsize; i++){
		(SMC_TBD_ADDR(wa) + i)->status = 0;
	}
	(SMC_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 = SMC_TBD_ADDR(wa);
		s = (unsigned char *)&wa->req_q;
		for(i = 0; i < tsize; i++){
			b->buf = s++;
			b->length = 1;
			b++;
		}
	}
}


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

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[n].work_area;
	quicc = smc->quicc;
	regs = (struct smc_regs *)wa->regs;

	issue_cmd(INIT_RXTX_PARAMS |
			(wa->channel ? 0x90 : 0xd0), quicc);

	regs->smc_smcmr = smc->smc_smcmr;
	regs->smc_smce = smc->smc_smce;
	regs->smc_smcm = smc->smc_smcm;
}
