/*********************************************************************** 
* 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:	intr.c
 *
 * Description:
 *	QUICC interrupt routines.
 *	These should all be called from user code
 *	via the GCT.
 *	All routines expect to be passed a pointer to
 *	internal RAM as a parameter.
 *
 * Routines:
 *	piopc0_intr
 *	scc1_intr
 *	scc2_intr
 *	scc3_intr
 *	scc4_intr
 *	piopc1_intr
 *	tim1_intr
 *	piopc2_intr
 *	piopc3_intr
 *	sdmaberr_intr
 *	dma1_intr
 *	dma2_intr
 *	tim2_intr
 *	cptim_intr
 *	piopc4_intr
 *	piopc5_intr
 *	tim3_intr
 *	piopc6_intr
 *	piopc7_intr
 *	piopc8_intr
 *	tim4_intr
 *	piopc9_intr
 *	spi_intr
 *	smc1_intr
 *	smc2_intr
 *	piopc10_intr
 *	piopc11_intr
 *	error_intr
 *
 * Author:
 *	Jonathan Masel
 ********************************************************/

#include "gct.h"
#include "modules.h"
#include "types.h"
#include "msg.h"
#include "mtypes.h"
#include "quicc.h"
#include "config.h"
#include "bss.h"
#include "mng.h"
#include "bisync.h"
#include "states.h"
#include "tm.h"
#include "dma.h"
#include "registers.h"

extern bisync_rxch(volatile QUICC_BD *bd, WORK_AREA *wa,
	unsigned short channel);

#define	CLEAR_BIT(x, bit)	x = bit


piopc0_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	intr_trace(QUICC_INTERRUPT, INTR_PIO_PC0);
	CLEAR_BIT(iram->intr_cisr, INTR_PIO_PC0);
}

scc1_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	intr_trace(QUICC_INTERRUPT, INTR_SCC1);
	scc_intr(i);
	CLEAR_BIT(iram->intr_cisr, INTR_SCC1);
}

scc2_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	intr_trace(QUICC_INTERRUPT, INTR_SCC2);
	scc_intr(i);
	CLEAR_BIT(iram->intr_cisr, INTR_SCC2);
}

scc3_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	intr_trace(QUICC_INTERRUPT, INTR_SCC3);
	scc_intr(i);
	CLEAR_BIT(iram->intr_cisr, INTR_SCC3);
}

scc4_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	intr_trace(QUICC_INTERRUPT, INTR_SCC4);
	scc_intr(i);
	CLEAR_BIT(iram->intr_cisr, INTR_SCC4);
}

piopc1_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	intr_trace(QUICC_INTERRUPT, INTR_PIO_PC1);
	CLEAR_BIT(iram->intr_cisr, INTR_PIO_PC1);
}

tim1_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	unsigned short timer_ter1;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	timer_ter1 = iram->timer_ter1;
	CLEAR_BIT(iram->timer_ter1, timer_ter1);
	intr_trace(QUICC_INTERRUPT, INTR_TIMER1);
	CLEAR_BIT(iram->intr_cisr, INTR_TIMER1);
}

piopc2_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	intr_trace(QUICC_INTERRUPT, INTR_PIO_PC2);
	CLEAR_BIT(iram->intr_cisr, INTR_PIO_PC2);
}

piopc3_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	intr_trace(QUICC_INTERRUPT, INTR_PIO_PC3);
	CLEAR_BIT(iram->intr_cisr, INTR_PIO_PC3);
}

/********************************************************
/*
 * i is the QUICC index
 */
sdmaberr_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	GWA *gwa;
	unsigned char	ssr;

	GETGCT(gct);
	gwa = (GWA *)gct->driver->work_area;
	iram = gwa->conf[i]->scc[0].quicc;
	intr_trace(QUICC_INTERRUPT, INTR_SDMA_BERR);
	ssr = iram->sdma_sdsr;
	CLEAR_BIT(iram->sdma_sdsr, ssr);
	CLEAR_BIT(iram->intr_cisr, INTR_SDMA_BERR);

	/*
	 * report to management
	 */
	{
	MSG *m;
	extern MSG *getm();

	m = getm(RESTART, (unsigned short)i, 0, 4);
	if( !m )
		return;
	m->param[0] = (int)gwa->conf[i];
	(*gct->send)(DRIVER, m);
	}
}

dma1_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;
	unsigned short idma1_csr;

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

	intr_trace(QUICC_INTERRUPT, INTR_DMA1);
	idma1_csr = iram->idma1_csr;

	if (idma1_csr & wa->intr_txbd)	{
		iram->idma1_cmar &= ~wa->intr_txbd;
		(*gct->send)(DRIVER, &wa->txconf_event);
	}

	if( idma1_csr & wa->intr_terr ) 
		(*gct->send)(DRIVER, &wa->txe_event);

	CLEAR_BIT(iram->idma1_csr, idma1_csr);
	CLEAR_BIT(iram->intr_cisr, INTR_DMA1);
}

dma2_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;
	unsigned short idma2_csr;

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

	intr_trace(QUICC_INTERRUPT, INTR_DMA2);
	idma2_csr = iram->idma2_csr;

	if (idma2_csr & wa->intr_txbd) {
		iram->idma2_cmar &= ~wa->intr_txbd;
		(*gct->send)(DRIVER, &wa->txconf_event);
	}

	if( idma2_csr & wa->intr_terr ) 
		(*gct->send)(DRIVER, &wa->txe_event);

	CLEAR_BIT(iram->idma2_csr, idma2_csr);
	CLEAR_BIT(iram->intr_cisr, INTR_DMA2);
}

tim2_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	unsigned short timer_ter2;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	timer_ter2 = iram->timer_ter2;
	CLEAR_BIT(iram->timer_ter2, timer_ter2);
	intr_trace(QUICC_INTERRUPT, INTR_TIMER2);
	CLEAR_BIT(iram->intr_cisr, INTR_TIMER2);
}

cptim_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	intr_trace(QUICC_INTERRUPT, INTR_PIO_PC0);
	CLEAR_BIT(iram->intr_cisr, INTR_PIO_PC0);
}

piopc4_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	intr_trace(QUICC_INTERRUPT, INTR_PIO_PC4);
	CLEAR_BIT(iram->intr_cisr, INTR_PIO_PC4);
}

piopc5_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	intr_trace(QUICC_INTERRUPT, INTR_PIO_PC5);
	CLEAR_BIT(iram->intr_cisr, INTR_PIO_PC5);
}

tim3_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	unsigned short timer_ter3;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	timer_ter3 = iram->timer_ter3;
	CLEAR_BIT(iram->timer_ter3, timer_ter3);
	intr_trace(QUICC_INTERRUPT, INTR_TIMER3);
	CLEAR_BIT(iram->intr_cisr, INTR_TIMER3);
}

piopc6_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	intr_trace(QUICC_INTERRUPT, INTR_PIO_PC6);
	CLEAR_BIT(iram->intr_cisr, INTR_PIO_PC6);
}

piopc7_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	intr_trace(QUICC_INTERRUPT, INTR_PIO_PC7);
	CLEAR_BIT(iram->intr_cisr, INTR_PIO_PC7);
}

piopc8_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	intr_trace(QUICC_INTERRUPT, INTR_PIO_PC8);
	CLEAR_BIT(iram->intr_cisr, INTR_PIO_PC8);
}

tim4_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	unsigned short timer_ter4;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	timer_ter4 = iram->timer_ter4;
	CLEAR_BIT(iram->timer_ter4, timer_ter4);
	intr_trace(QUICC_INTERRUPT, INTR_TIMER4);
	CLEAR_BIT(iram->intr_cisr, INTR_TIMER4);
}

piopc9_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	intr_trace(QUICC_INTERRUPT, INTR_PIO_PC9);
	CLEAR_BIT(iram->intr_cisr, INTR_PIO_PC9);
}

spi_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;
        unsigned char event;
	MSG *m;
	extern MSG *getm();

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
        event = iram->spi_spie;
        if (event & RXB)
		(*gct->send)(DRIVER,&wa->rxind_event);
        if (event & TXB)
		(*gct->send)(DRIVER,&wa->txconf_event);
        if (event & TXE)
		(*gct->send)(DRIVER,&wa->txe_event);
        if (event & BSY){
		/* CLEAR the BUSY bit in mask reg */
		wa->quicc->spi_spim &= ~BSY; 
		(*gct->send)(DRIVER,&wa->busy_event);
	}
        if (event & MME){
		/* relm it in the rotine */
		if(m = getm(MULTI_MASTER_ERR,(unsigned short)i, 0,0))
			(*gct->send)(DRIVER,m);
	}
	CLEAR_BIT(iram->spi_spie, event);
	intr_trace(QUICC_INTERRUPT, INTR_SPI);
	CLEAR_BIT(iram->intr_cisr, INTR_SPI);
}

smc1_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;
        unsigned char event;
	register struct smc_regs *regs;

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

	intr_trace(QUICC_INTERRUPT, INTR_SMC1);

        event = regs->smc_smce;
	/* only consider unmasked interrupts */
	event &= regs->smc_smcm;

	/*
	 * first send primitive event notifications.
	 * only issue the protocol interrupts once:
	 * the module will re-enable them
	 */
        if (event & wa->intr_txbd) {
                regs->smc_smcm &= ~wa->intr_txbd;
		(*gct->send)(DRIVER, &wa->txconf_event);
	}

        if (event & wa->intr_rxfr) {
                regs->smc_smcm &= ~wa->intr_rxfr;
		(*gct->send)(DRIVER, &wa->rxind_event);
	}

        if (event & wa->intr_busy) {
                regs->smc_smcm &= ~wa->intr_busy;
		(*gct->send)(DRIVER, &wa->busy_event);
	}

        if (event & wa->intr_terr) 
		(*gct->send)(DRIVER, &wa->txe_event);

	/*
	 * clear handled event bits
	 */
	CLEAR_BIT(regs->smc_smce, event);

	CLEAR_BIT(iram->intr_cisr, INTR_SMC1);
}

smc2_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;
        unsigned char event;
	register struct smc_regs *regs;

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

	intr_trace(QUICC_INTERRUPT, INTR_SMC2);

        event = regs->smc_smce;
	/* only consider unmasked interrupts */
	event &= regs->smc_smcm;

	/*
	 * first send primitive event notifications.
	 * only issue the protocol interrupts once:
	 * the module will re-enable them
	 */
        if (event & wa->intr_txbd) {
                regs->smc_smcm &= ~wa->intr_txbd;
		(*gct->send)(DRIVER, &wa->txconf_event);
	}

        if (event & wa->intr_rxfr) {
                regs->smc_smcm &= ~wa->intr_rxfr;
		(*gct->send)(DRIVER, &wa->rxind_event);
	}

        if (event & wa->intr_busy) {
                regs->smc_smcm &= ~wa->intr_busy;
		(*gct->send)(DRIVER, &wa->busy_event);
	}

        if (event & wa->intr_terr) 
		(*gct->send)(DRIVER, &wa->txe_event);

	/*
	 * clear handled event bits
	 */
	CLEAR_BIT(regs->smc_smce, event);

	CLEAR_BIT(iram->intr_cisr, INTR_SMC2);
}

piopc10_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	intr_trace(QUICC_INTERRUPT, INTR_PIO_PC10);
	CLEAR_BIT(iram->intr_cisr, INTR_PIO_PC10);
}

piopc11_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	intr_trace(QUICC_INTERRUPT, INTR_PIO_PC11);
	CLEAR_BIT(iram->intr_cisr, INTR_PIO_PC11);
}

error_intr(i)
{
	GCT *gct;
	register QUICC *iram;
	WORK_AREA *wa;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[i].work_area;
	iram = wa->quicc;
	intr_trace(QUICC_INTERRUPT, INTR_ERR);
	CLEAR_BIT(iram->intr_cisr, INTR_ERR);
}

/********************************************************
 * routine:	scc_intr
 *
 * description:
 *	Called by the sccN_intr routine.
 *	Sends the appropriate message to the driver module.
 *	A message from an interrupt routine consists
 *	of a 32-bit number (NOT a pointer to a MSG)
 *	defined as follows:
 *		16 MSBs = interrupt_tye
 *		16 LSBs = channel number
 *	(these correspond to the type and id fields of
 *	a HDR structure).
 *	a proper MSG structure is sent only to the SYS_MNG module
 *	for non-primitive event notification.
 *	Note: Two variables are used to store the event register.
 *	One (event) is used for event notification: all masked
 *	event bits are cleared here before examining its contents.
 *
 * arguments:
 *	i		the channel number (0 - 3)
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/

scc_intr(i)
int i;
{
	GCT *gct;
	struct child *child;
	register WORK_AREA *wa;
	register unsigned short event;
	register struct scc_regs *regs;
	register unsigned long mode;
	int j;
	unsigned short cmd;
	register QUICC_BD *bd1, *bd2;

	GETGCT(gct);
	child = &gct->driver->child[i];
	wa = (WORK_AREA *)child->work_area;
	regs = wa->regs;
	event = regs->scc_scce;
	/* only consider unmasked interrupts */
	event &= regs->scc_sccm;

	mode = regs->scc_gsmra;

	/*
	 * for bisync channels, we need the
	 * last BD received by the QUICC (bd1)
	 * and the next BD to be used (bd2)
	 */
	if( (mode&MODE) == BISYNC_PORT ){
		bd2 = wa->quicc_rbd;
		if( bd2->status & R_E ){
			bd1 = 0;
		} else {
			do {
				bd1 = bd2;
				if( bd1->status & R_W )
					bd2 = RBD_ADDR(wa);
				else
					bd2++;
				if( bd2 == wa->quicc_rbd )
					break;
			} while( !(bd2->status & R_E) );
		}
	}

	/*
	 * first send primitive event notifications.
	 * only issue the protocol interrupts once:
	 * the module will re-enable them
	 */
	if( event & wa->intr_txbd ){
		regs->scc_sccm &= ~wa->intr_txbd;
		(*gct->send)(DRIVER, &wa->txconf_event);
	}
	if( event & wa->intr_rxfr ){
		if( (mode&MODE) == BISYNC_PORT ){
			/*
			 * for bisync channels, frames end
			 * in either BCS (R_B is set) or
			 * a control character (R_C is set).
			 * for bisync channels, the rxbd interrupt
			 * is not masked: interrupts are always
			 * handled.
			 */
			if( bd1 ){
				if( bd1->status & (R_C | R_B) ){
					/* if end of frame ... */
					event |= BISYNC_RCHR;
					regs->scc_sccm |= BISYNC_RCHR;
					regs->scc_gsmra &= ~(BI_TRANSP | BI_BCS);
					(*gct->send)(DRIVER, &wa->rxind_event);
				}
			}
			goto nofr;
		}

		regs->scc_sccm &= ~wa->intr_rxfr;
		(*gct->send)(DRIVER, &wa->rxind_event);
	}
nofr:

	/*
	 * notify driver module of relevant events
	 */
	if( event & wa->intr_busy ){
		regs->scc_sccm &= ~wa->intr_busy;
		(*gct->send)(DRIVER, &wa->busy_event);
	}
	if( event & wa->intr_terr )
		(*gct->send)(DRIVER, &wa->txe_event);

	/*
	 * for bisync channels, check the rx-character mask
	 */
	if( (mode&MODE) == BISYNC_PORT )
		if( (event & BISYNC_RCHR) && (bd2->length > 0) )
			bisync_rxch(bd2, wa, (unsigned short)i);

	/*
	 * cleared handled event bits
	 */
	CLEAR_BIT(regs->scc_scce, event);



	if( (event & (TM_TBD | TM_TXE | TM_GRA | TM_DCC | TM_GLt)) &&
		regs->scc_gsmrb & TTX ) 
		j = ETHERNET_PORT+1;
	else if( (event & (TM_RBD | TM_BSY | TM_GLr) &&
		regs->scc_gsmrb & TRX) )
		j = ETHERNET_PORT+1;
	else	j = mode&MODE;
	intr_trace(SCC_INTERRUPT, event, i, j);
}

intr_trace(type, event, i, mode)
{
	register MSG *m;
	EVENT_INFO *e;
	GCT *gct;
	register WORK_AREA *wa;
	GWA *gwa;
	extern MSG *getm();

	GETGCT(gct);
	gwa = (GWA *)gct->driver->work_area;
	wa = (WORK_AREA *)gct->driver->child[i].work_area;

	/*
	 * notify management of the event
	 */
	if( ((type == SCC_INTERRUPT) && (wa->intr_trace & event))
	 || ((type == QUICC_INTERRUPT) && (gwa->intr_trace & event)) ){
		m = getm(INTERRUPT, (unsigned short)i, 0,
				sizeof(EVENT_INFO)-EV_DATA_SIZE);
		if( m ){
			e = (EVENT_INFO *)m->param;
			e->type = type;
			e->par = event;
			e->src = mode;
			(*gct->send)(SYS_MNG, m);
		}
	}
}
