/*********************************************************************** 
* 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:	spi.c
 *
 * Description:
 *	The driver routines for handling an SPI port.
 *
 * Routines:
 *	spi_init
 *	spi_txreq
 *	spi_txerr
 *	spi_rxbuf
 *
 * Author:
 *	Jonathan Masel
 ********************************************************/

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

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

/********************************************************
 * routine:	spi_init
 *
 * description:
 *	Initialize the driver tables for
 *	SPI ports.
 *
 * arguments:
 *	spi 		The configured spi driver ( One RxDB, One TxDB )
 * return code:
 *
 * side effects:
 *
 ********************************************************/
spi_init(spi)
struct spi *spi;
{
	int i;
	GCT *gct;
        QUICC *quicc;
	int (* *protocol)();
	unsigned adr, rom;
        CHILD *child;
	WORK_AREA *wa;
       	struct spi_pram *spi_pram;
        R_FRAME *r;
        extern R_FRAME *getr();
	extern int spi_txreq(), spi_rxbuf(),spi_conf(),spi_txerr();
	extern int spi_busy(), spi_multi_master();

	GETGCT(gct);
	
	/* Init state machine */

	protocol = gct->driver->protocol;
	rom = gct->driver->rom;
	adr = (unsigned)spi_txreq + rom;
	protocol[SUB(TX_REQ) | SPI_STATE] = (int (*)())adr;
	adr = (unsigned)spi_conf + rom;
	protocol[SUB(TX_CONF) | SPI_STATE] = (int (*)())adr;
	adr = (unsigned)spi_rxbuf + rom;
	protocol[SUB(RX_IND) | SPI_STATE] = (int (*)())adr;
	adr = (unsigned)spi_txerr + rom;
	protocol[SUB(TX_ERR)| SPI_STATE] = (int (*)())adr;
        adr = (unsigned)spi_busy + rom;
        protocol[SUB(BUSY) | SPI_STATE] = (int (*)())adr;
        adr = (unsigned)spi_multi_master + rom;
        protocol[SUB(MULTI_MASTER_ERR) | SPI_STATE] = (int (*)())adr;
 

        child = &gct->driver->child[spi->child_id];
        child->key = spi->child_id;
        child->flags = 0;
	child->upper = (int)spi->upper;
	child->state = SPI_STATE;
	child->smac = &protocol[SPI_STATE];

        wa = (WORK_AREA *)child->work_area;
        quicc = spi->quicc;
	if( !quicc)
		quicc = wa->quicc;
	else
		wa->quicc = quicc;

	/*
	 * initialize work area
	 */
	(struct spi_pram *)wa->pram =&quicc->pram[1].scc.pothers.timer_spi.spi; /* second memory page */
	wa->tcount = wa->rcount = 0;
	wa->rxind_event = (RX_IND << 16) | spi->child_id; 
     	wa->txconf_event = (TX_CONF << 16) | spi->child_id;
        wa->txe_event = (TX_ERR << 16) | spi->child_id;
        wa->busy_event = (BUSY<< 16) | spi->child_id;

	/* 
	 * Initialize parameter ram  
	 */

        spi_pram = (struct spi_pram *)(wa->pram);
        spi_pram->rbase = spi->rbase;
        spi_pram->tbase = spi->tbase;
        spi_pram->rfcr = spi_pram->tfcr = NORMAL_MODE ; 
        spi_pram->mrblr = spi->mrblr;


        /*
         * disable transmit/receive
         */
        quicc->spi_spim = 0;

	/* initialize BD's */
	/* prepare first receive buffer */
	if(!(r = getr(RX | BUFFER | GLOBAL_SCC, spi->child_id)))
		return; /* no receive buffers availbale */
        RBD_ADDR(wa)[0].buf = r->buf;
        RBD_ADDR(wa)[0].length = 0;
        RBD_ADDR(wa)[0].status = R_E | R_W | R_I;


	wa->rx_q = r;        
	wa -> rx_q->flags = 0; /* must ZERO RX_POOL flags */ 

        issue_cmd(INIT_RXTX_PARAMS | SPI_MODE, quicc);

        quicc->spi_spmode = spi->spi_spmode;
        quicc->spi_spie = CLEAR_SPIE;
        quicc->spi_spim = spi->spi_spim;
        quicc->spi_spcom = 0;


	return(SUCCESS);
}



/********************************************************
 * routine:	spi_txreq
 *
 * description:
 *	Transmit the byte of data on the SPI channel.
 *
 * arguments:
 *	data		the data byte for transmission
 *
 * side effects:
 *
 ********************************************************/
spi_txreq(t, child)
T_FRAME *t;
CHILD *child;
{
	register WORK_AREA *wa;
        register LAYER_INFO *l;

	wa = (WORK_AREA *)child->work_area;
	l = &t->layer[LAYER(DRIVER)];

        TBD_ADDR(wa)[0].length = t->bsize - l->boff;
        TBD_ADDR(wa)[0].buf = &(t->buf[l->boff]);
        TBD_ADDR(wa)[0].status = T_R | T_W | T_I | T_L;

	/* transmission requested via SPCOM register */
	wa->quicc->spi_spcom = SPI_START;
	/* Next receive will release this buffer or the TXB interrupt*/
        wa->conf_q = t; 
}

/********************************************************
 * routine:	spi_conf
 *
 * description:
 *		Realese the T_FRAME msg back to memory
 * side effects:
 *
 ********************************************************/
spi_conf(h, child)
HDR	*h;
CHILD *child;
{
	register WORK_AREA *wa;

	wa = (WORK_AREA *)child->work_area;
	conf_msg((HDR *)wa->conf_q, child->upper);
		
}


/********************************************************
 * routine:	spi_txerr
 *
 * description:
 *		An Error accured on transmition - Retransmit buffer
 * side effects:
 *
 ********************************************************/
spi_txerr(h, child)
HDR	*h;
CHILD *child;
{
	register WORK_AREA *wa;

	/* clear event register and try to send the buffer again */
	wa = (WORK_AREA *)child->work_area;
        wa->quicc->spi_spie |= TXE;
	spi_txreq(wa->conf_q, child);
		
}


/********************************************************
 * routine:	spi_rxbuf
 *
 * description:
 *	Collect a received data bytes on the SPI port.
 *
 * side effects:
 *
 ********************************************************/
spi_rxbuf(h, child)
HDR	*h;
CHILD *child;
{
	register WORK_AREA *wa;
        R_FRAME *r;
        GCT *gct;
        extern R_FRAME *getr();

        GETGCT(gct);
	wa = (WORK_AREA *)child->work_area;

	if(RBD_ADDR(wa)[0].status & R_E)
		return(0);
	r = wa->rx_q;
        r->flen = r->blen = RBD_ADDR(wa)[0].length;
	if( (*gct->send)(child->upper, r) )
		relm(r);

	/* prepare next BD buffer to receive */
	/* Assumption Upper will relm the MSG */
	if (! (r = getr(RX | BUFFER | GLOBAL_SCC, child->key))){
		wa->rx_q = (R_FRAME *)(0);
		return; /* no receive buffers availbale */
	}
	wa -> rx_q = r;
	wa -> rx_q->flags = 0; /* must ZERO RX_POOL flags */ 
        RBD_ADDR(wa)[0].buf = r->buf;
        RBD_ADDR(wa)[0].status = R_E | R_W | R_I;

}

/********************************************************
 * routine:	spi_multi_master
 *
 * description:
 *			Multi master error accured, clear EN bit at 
 *			the SPMOD register and act as RESET
 *
 * side effects:
 *
 ********************************************************/
spi_multi_master(h, child)
HDR	*h;
CHILD 	*child;
{
	register WORK_AREA *wa;
	GCT *gct;
 	GWA *gwa;
	struct driver_config *driver;
	int index;

	GETGCT(gct);
	wa = (WORK_AREA *)child->work_area;
        wa->quicc->spi_spmode &= 0xfeff; /* Clear the EN bit in SPMODE */
        gwa = (GWA *)gct->driver->work_area;
	/* find out witch quicc need to be RESET */
	for(index=0;;index++){
        	driver = ((GWA *)gct->driver->work_area)->conf[index];
		if(wa->quicc == driver->internal_ram)
			break;
	}
	spi_init(driver->spi);
	/* relm the MSG */
	relm(h);
}
/********************************************************
 * routine:	spi_busy
 *
 * description:
 *			Busy event accured, mask the SPIM reg,
 *			close receive buffer & send it to upper
 *			send CNTL msg BUSY to upper
 * side effects:
 *
 ********************************************************/
spi_busy(h,child)
HDR	*h;
CHILD *child;
{
	register WORK_AREA *wa;
	GCT *gct;
        MSG *m;
        R_FRAME *r;
        extern MSG *getm();

	wa = (WORK_AREA *)child->work_area;

	/* send busy msg to upper */
	GETGCT(gct);
        m = getm(BUSY_IND, h->id, 0, 0);
        if( m ){
       	      m->hdr.status = LOCAL_BUSY_DETECTED;
       	      if( (*gct->send)(child->upper, m) )
       			   relm((HDR *)m);
      	};
	/* case that rxconf couldn't allocate buffer for receive 
	 * busy will allocate it for him
	*/
	if( !( wa->rx_q )) {
		if (! (r = getr(RX | BUFFER | GLOBAL_SCC, child->key)))
			return; /* no receive buffers availbale */
		wa -> rx_q = r;
		wa -> rx_q->flags = 0; /* must ZERO RX_POOL flags */ 
        	RBD_ADDR(wa)[0].buf = r->buf;
        	RBD_ADDR(wa)[0].status = R_E | R_W | R_I;
	}

}
