/*
 *  Copyright (c) 1996-2007 Digi International Inc., All Rights Reserved
 *
 *  This software contains proprietary and confidential information of Digi
 *  International Inc.  By accepting transfer of this copy, Recipient agrees
 *  to retain this software in confidence, to prevent disclosure to others,
 *  and to make no use of this software other than that for which it was
 *  delivered.  This is an unpublished copyrighted work of Digi International
 *  Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
 *  prohibited.
 *
 *  Restricted Rights Legend
 *
 *  Use, duplication, or disclosure by the Government is subject to
 *  restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
 *  Technical Data and Computer Software clause at DFARS 252.227-7031 or
 *  subparagraphs (c)(1) and (2) of the Commercial Computer Software -
 *  Restricted Rights at 48 CFR 52.227-19, as applicable.
 *
 *  Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
 *
 *  spi_slave.c This file contains the SPI Slave Driver.
 *
 *
 * Edit History
 *
 * Date      Initials       Change Description
 * ---------------------------------------------------------------------
 */

#include <string.h>
#include "netoserr.h"
#include "bum_reg.h"
#include "gpiomux_def.h"

#include "nammu.h"
#include "nainvalidate.h"
#include "bsp_serial.h"
#include "reg_def.h"
#include "NA_ISR.h"
#include "spi_slave.h"
#include "tx_api.h"

/*ad*
 *
 * Allows a SPI port to emulate one slave 
 * device in a single application thread. 
 *
 * Applications supply the driver with transmit and receive buffers.
 * The driver returns the buffers back to the application as they are consumed.
 *
 * The simple SPI slave driver API functions include:
 *
 * <list type="unordered">
 *		<item>@link NASpiSlaveInitialize  </item>
 *		<item>@link NASpiSlaveEnqueueBuffer  </item>
 * </list>
 *
 *
 * @note	The simple SPI Slave driver API supports onlt the NS9750 and NS9360. The 
 *          SPI slave is fixed to an active low chip select by the NS9750 and NS9360 hardware
 *          design.
 *
 * @external
 * @overview SPI Slave Driver
 * @category BSP:Device_Drivers:SPI:SPISlaveAPI
 * @include 	"spi_slave_api.h"
 * @library 	bsp
 * @platform 	ns9360_a,ns9750_a,connectcore9c_a
 * @since 6.2
*ad*/

#define INTERRUPT_SAVE_AREA int interrupt_save
#define DISABLE_INTS        interrupt_save = NASetIRQ(IRQ_DISABLED)
#define RESTORE_INTS        NASetIRQ(interrupt_save)


#define QSUCCESS  0
#define QFAIL    -1
/*
 * Serial port registers' starting address
 */
static SPIREG_TYPE *spiRegs[NASPI_MAX_MASTER_PORT] = {
    (SPIREG_TYPE *) NARM_SC1CRA_ADDR,
    (SPIREG_TYPE *) NARM_SC2CRA_ADDR,
    (SPIREG_TYPE *) NARM_SC3CRA_ADDR,
    (SPIREG_TYPE *) NARM_SC4CRA_ADDR
};

/*
 * Serial port TX and RX DMA registers' starting address
 */
static DMAREG_TYPE *dmaRegs[NASPI_MAX_MASTER_PORT] = {

    (DMAREG_TYPE *) 0x90000040,
    (DMAREG_TYPE *) 0x90000000,
    (DMAREG_TYPE *) 0x90000080,
    (DMAREG_TYPE *) 0x900000c0,
};





static SPISLAVE_PORT_TYPE spiSlavePorts[NASPI_MAX_SLAVE_PORT];


/*
 * Cache related info for ns9750 and ns9360
 * rx_assoc holds the virtual addresses of the rx descriptor data buffers.
 * tx_assoc holds the virtual addresses of the tx descriptor data buffers.
 */
static unsigned long rx_assoc[NASPI_MAX_SLAVE_PORT];
static unsigned long tx_assoc[NASPI_MAX_SLAVE_PORT];
static NA_DMA_DESCRIPTOR_TYPE txc_buffer_descriptors[NASPI_MAX_SLAVE_PORT];
static NA_DMA_DESCRIPTOR_TYPE rxc_buffer_descriptors[NASPI_MAX_SLAVE_PORT];

/*
 * Local functions
 */
static void spi_slave_rx_dma_int (int port);
static void spiSlaveReset(int port);
static void setupDma (int port, char *const writeBuffer, char *readBuffer, int bufferLength);
static void enablePort (int port);
static void disablePort (int port);
static void initSpiPort (int port);
static void initDma (int port);
static void setupSpiPort (int port);
static SPISLAVE_BUFFER_T * spiSlaveDeque(SPISLAVE_BUFFER_Q * pQ);
static int spiSlaveEnque(SPISLAVE_BUFFER_Q * pQ, SPISLAVE_BUFFER_T *pBuffer);
static void spiSlaveInitQ(SPISLAVE_BUFFER_Q * pQ, int size);
#define MAX_Q_SIZE NASPI_MAX_SLAVE_RING_SIZE + 1
static SPISLAVE_BUFFER_TABLE_T pBufferTable[MAX_Q_SIZE];
static SPISLAVE_BUFFER_T * mallocPB (void);
static void freePB (SPISLAVE_BUFFER_T *pbuffer);


/*
 * This is the initialization function called from netosStartup() in BSP
 * according to the serial port settings in bsp.h in each platform.
 *
 *  Parameters:
 *		port		spi slave port number, 0, 1, 2, or 3
 *
 *  Return Values:
 *		none
 *	   
 */		
NaStatus initializeSpiSlave (void)
{
    NA_UNUSED_PARAM(initSpiPort);  /* remove the GNU warning of not using initSpiPort*/
    memset (spiSlavePorts, 0, sizeof spiSlavePorts);
#if (BSP_SERIAL_PORT_1 == BSP_SERIAL_SPI_SLAVE_DRIVER)
    initSpiPort (0);
#endif

#if (BSP_SERIAL_PORT_2 == BSP_SERIAL_SPI_SLAVE_DRIVER)
    initSpiPort (1);
#endif

#if (BSP_SERIAL_PORT_3 == BSP_SERIAL_SPI_SLAVE_DRIVER)
    initSpiPort (2);
#endif

#if (BSP_SERIAL_PORT_4 == BSP_SERIAL_SPI_SLAVE_DRIVER)
    initSpiPort (3);
#endif

    return NASTATUS_SUCCESS;
}

/*
 * This routine initializes a spi port, called by initializeSpiSlave() during BSP start up
 *
 *  Parameters:
 *		port		spi slave port number, 0, 1, 2, or 3
 *
 *  Return Values:
 *		none
 *	   
 */		
static void initSpiPort (int port)
{
    int rx_int_id=0, tx_int_id=0;


    switch (port)
    {
    case 0:
        rx_int_id = BBUS_DMA03_INTERRUPT;
        tx_int_id = BBUS_DMA04_INTERRUPT;
        /*dout=8;
           din=9;
           clk=14;
	   enable = 15;*/
        /* set endian */
        narm_write_reg(NA_BUM_ECR_REG, NA_BUM_ECR_ADDRESS, ser2, 0);
        break;
    case 1:
        rx_int_id = BBUS_DMA01_INTERRUPT;
        tx_int_id = BBUS_DMA02_INTERRUPT;

        /*dout=0;
           din=1;
           clk=6;
	   enable = 7;*/
        /* set endian */
        narm_write_reg(NA_BUM_ECR_REG, NA_BUM_ECR_ADDRESS, ser1, 0);
        break;
    case 2:
        rx_int_id = BBUS_DMA05_INTERRUPT;
        tx_int_id = BBUS_DMA06_INTERRUPT;
	/* dout=40;
           din=41;
           clk=22; 
	   enable = 23;*/
        /* set endian */
        narm_write_reg(NA_BUM_ECR_REG, NA_BUM_ECR_ADDRESS, ser3, 0);
        break;
    case 3:
        rx_int_id = BBUS_DMA07_INTERRUPT;
        tx_int_id = BBUS_DMA08_INTERRUPT;
	/* dout=44;
           din=45;
           clk=26;
	   enable = 27;*/
        /* set endian */
        narm_write_reg(NA_BUM_ECR_REG, NA_BUM_ECR_ADDRESS, ser4, 0);
        break;
    }
 
    spiSlavePorts[port].tx_buffer_descriptors = NA_UNCACHED (&txc_buffer_descriptors[port]);
    spiSlavePorts[port].rx_buffer_descriptors = NA_UNCACHED (&rxc_buffer_descriptors[port]);

    

    NAInstallIsr (rx_int_id, (NA_ISR_HANDLER) spi_slave_rx_dma_int, (void *) port);

	/*
     * disable interrupt
     */
    NADisableIsr (spiSlavePorts[port].rx_int_id);
	NADisableIsr (spiSlavePorts[port].tx_int_id);

    spiSlavePorts[port].opened = 1;
    spiSlavePorts[port].spiRegs = spiRegs[port];
    spiSlavePorts[port].dmaRegs = dmaRegs[port];
    spiSlavePorts[port].rx_int_id = rx_int_id;
	spiSlavePorts[port].tx_int_id = tx_int_id;
#if 0
    /* reset SPI */
    narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmarcr, mustSetToZero, 1);      /* Rx reset */
    narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmatcr, mustSetToZero, 1);      /* Tx reset */

//    NAWait(1);

    narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmarcr, mustSetToZero, 0);      
    narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmatcr, mustSetToZero, 0);      

    narm_write_reg (NARM_SCRA_REG, &spiSlavePorts[port].spiRegs->ctrA, ce, 0);       /*disable the port */

    narm_write_reg (NARM_SCRA_REG, &spiSlavePorts[port].spiRegs->ctrA, wls, 3);      /*word length of 8 data bits */
    narm_write_reg (NARM_SCRA_REG, &spiSlavePorts[port].spiRegs->ctrA, rl, 0);       /*disable remote loop back */
    narm_write_reg (NARM_SCRA_REG, &spiSlavePorts[port].spiRegs->ctrA, ll, 0);       /*disable remote loop back */
    narm_write_reg (NARM_SCRA_REG, &spiSlavePorts[port].spiRegs->ctrA, erxdma, 0);   /*enable receive DMA */
    narm_write_reg (NARM_SCRA_REG, &spiSlavePorts[port].spiRegs->ctrA, etxdma, 0);   /*enable transmit DMA */
#endif
    /*
     * setup serial control register B
     */
    narm_write_reg (NARM_SCRB_REG, &spiSlavePorts[port].spiRegs->ctrB, mode, 3);     /*SPI slave mode */
}

/*ad*
 * Initializes a SPI port as SPI slave, sets up the port parameters such as the SPI mode and bit order,
 * and initializes the receive buffer ring and transmit buffer ring. 
 *
 * @category BSP:Device_Drivers:SPI:SPISlaveAPI
 * @since 6.2
 * @external
 *
 * @param port				0-based port number.
 * @param bufferRingSize	The size of the transmitter/receiver buffer ring.
 * @param mode				SPI mode.
 * @param bitOrder		    Set to either NA_SPI_MSB or NA_SPI_LSB
 *
 * @return NASTATUS_SPI_SLAVE_INVALID_SETTINGS	Invalid parameters.
 * @return NASTATUS_NOT_INITIALIZED		        The initialization failed.
 * @return NASTATUS_SPI_SLAVE_ACTIVE		The slave driver already has been initialized for this port.
 * @return NASTATUS_SUCCESS		                The SPI slave driver was successfully initialized.
 * 
 *
 * @see @link NASpiMode
 * @see @link NASpiBitOrder
 * @see @link NaStatusValues
*ad*/
NaStatus NASpiSlaveInitialize(int port, int bufferRingSize, NASpiMode mode, NASpiBitOrder bitOrder)
{

    NaStatus  ret = NASTATUS_SPI_SLAVE_INVALID_SETTINGS;

    if ((port < 0) || (port >= NASPI_MAX_SLAVE_PORT))
        goto Exit;

    if (bufferRingSize > NASPI_MAX_SLAVE_RING_SIZE)
        goto Exit;

	if (!spiSlavePorts[port].opened)
	{
        ret = initializeSpiSlave();
        if (ret != NASTATUS_SUCCESS)
        {
//		ret = NASTATUS_NOT_INITIALIZED;
		goto Exit;
        }
	}

	if (spiSlavePorts[port].initialized)
	{
		ret = NASTATUS_SPI_SLAVE_ACTIVE;
		goto Exit;
	}
	
	spiSlavePorts[port].initialized = 1;
	spiSlavePorts[port].dmaInuse = 0;
	

	disablePort(port);

	spiSlavePorts[port].spiMode = mode;
	spiSlavePorts[port].bitOrder = bitOrder;

	setupSpiPort (port);
	initDma(port);

	 

    spiSlaveInitQ(&spiSlavePorts[port].bufferQ, bufferRingSize+1);

	memset (pBufferTable, 0, sizeof pBufferTable);
	 
	ret = NASTATUS_SUCCESS;
  Exit:
    return ret;
}

  

/*ad*
 * Enqueues a pair of transmit/receive buffers into the transmission/receive buffer ring. 
 *
 * Transmit buffers are transmitted when the master device selects the port and supplies a clock. Transmit buffers
 * are returned to the application using the callback function as they are emptied.
 *
 * Receive buffers are filled when the master device selects the port and supplies a clock. Receive buffers
 * are returned to the application using the callback function as they are filled.
 *
 * It is safe to call this routine from user callback function, which can re-enqueue the retired buffer
 * after it proccesses the buffer.
 * 
 *
 * @category BSP:Device_Drivers:SPI:SPISlaveAPI
 * @since 6.2
 * @external
 *
 * @param port 			The SPI port number.
 *
 * @param buffer		Pointer to a NASpiSlaveBuffertype structure. The structure contains:
 *						<list type="unordered">
 *							<item>Information of transmit/receive data buffers</item>
 *							<item>The data length</item>
 *							<item>A callback function, which is called when the transmit and receive are completed</item>
 *						</list>
 *
 *
 * @return NASTATUS_SPI_SLAVE_INVALID_SETTINGS		Invalid arguments.
 * @return NASTATUS_NOT_INITIALIZED		The port has not been initialized.
 * @return NASTATUS_SPI_SLAVE_RING_FULL			The buffer queue is full.
 * @return NASTATUS_SUCCESS				The buffer has been enqueued.
 * 
 *
 * @see @link NASpiSlaveBufferType
 * @see @link NaStatusValues
*ad*/
NaStatus NASpiSlaveEnqueueBuffer(int port, NASpiSlaveBufferType * buffer)
{
    INTERRUPT_SAVE_AREA;
    NaStatus ret = NASTATUS_SPI_SLAVE_INVALID_SETTINGS;
	SPISLAVE_BUFFER_T *pBuffer=NULL;
 

    if ((port < 0) || (port >= NASPI_MAX_SLAVE_PORT))
	{
        goto Exit;
	}

	if ((buffer == NULL) || (buffer->writeBuffer == NULL) || (buffer->readBuffer == NULL) || 
		(buffer->bufferLength > NASPI_MAX_SLAVE_DATA_SIZE) ||
		((int)buffer->readBuffer %4))
	{
        goto Exit;
	}


	if (!spiSlavePorts[port].initialized)
	{
		ret = NASTATUS_NOT_INITIALIZED;
		goto Exit;
	}

	pBuffer = (SPISLAVE_BUFFER_T *)mallocPB ();
	if (pBuffer == NULL)
	{
		ret = NASTATUS_RESOURCE_ERROR;
		goto Exit;
	}
    
	pBuffer->buffer = buffer;
	pBuffer->next = NULL;

	DISABLE_INTS;
	if (!spiSlavePorts[port].dmaInuse && !spiSlavePorts[port].bufferQ.head)
	{
		spiSlaveReset(port);
	}

	if (spiSlaveEnque(&spiSlavePorts[port].bufferQ, pBuffer) == QFAIL)
	{
		RESTORE_INTS;
		ret = NASTATUS_SPI_SLAVE_RING_FULL;
		goto Exit;

	}


	/*If DMA is transfering or this buffer already finished*/
	if (spiSlavePorts[port].dmaInuse || spiSlavePorts[port].bufferQ.head == 0)
	{
		RESTORE_INTS;
	}
	else
	{

		setupDma (port, buffer->writeBuffer, buffer->readBuffer, buffer->bufferLength);
		enablePort(port);

		spiSlavePorts[port].dmaInuse = 1;
		RESTORE_INTS;
	}
	

	ret = NASTATUS_SUCCESS;

Exit:

	return ret;
}

/*
 *	This routine load the application buffer into dma
 *
 *  Parameters:
 *		port		 spi slave port number, 0, 1, 2, or 3
 *      writeBuffer  pointer to write buffer
 *      readBuffer   pointer to read buffer
 *      bufferLength the length of the read buffer.
 *
 *  Return Values:
 *		none
 *	   
 */		
static void setupDma (int port, char *const writeBuffer, char *readBuffer, int bufferLength)
{

    INTERRUPT_SAVE_AREA;
    physical_address_t physTx, physRx; 
    NA_DMA_DESCRIPTOR_TYPE *txLast;
    NA_DMA_DESCRIPTOR_TYPE *rxLast;

    /*
     * TX
     */
    /*Convert to a physical address */
    if (!NAVaToPhys ((const void *) writeBuffer, &physTx))
        netosFatalError ("spi slave tx DMA Cache error", 1, 1);

    /*Save virtual address for tx complete */
    tx_assoc[port] = (unsigned long) writeBuffer;

    /*
     * RX
     */

    /*Convert to physical address */
    if (!NAVaToPhys ((const void *) readBuffer, &physRx))
        netosFatalError ("spi slave rx DMA Cache error", 1, 1);

    /*Save the virtual address for later */
    rx_assoc[port] = (unsigned long) readBuffer;

    NABeforeDMA (NA_MEMORY_TO_DEVICE_DMA, writeBuffer, NULL, bufferLength);
    NABeforeDMA (NA_DEVICE_TO_MEMORY_DMA, NULL, readBuffer, bufferLength);

    /*
     * setup buffer descriptor
     */
	 
		txLast = spiSlavePorts[port].tx_buffer_descriptors;
		rxLast = spiSlavePorts[port].rx_buffer_descriptors;

        /*Setup the tx/rx buffer descriptor, and use the physical address in the DMA buffer descriptor */
		txLast->src_addr = physTx; 
		rxLast->src_addr = physRx;  
        txLast->dst_addr = 0;
        rxLast->dst_addr = 0;
        txLast->buf_len = (bufferLength <= 32)? 64 : bufferLength;
        rxLast->buf_len = bufferLength;
         
		txLast->flag &= DMA_WRAP_MASK | DMA_LAST_MASK; /*clear all the bit but not the wrap bit*/
        rxLast->flag &= DMA_WRAP_MASK | DMA_LAST_MASK | DMA_FULL_MASK; /*clear all the bit but not the wrap bit*/
	

	/*
	 * Start DMA
	 */
	DISABLE_INTS;

    txLast->flag |=  DMA_FULL_MASK;		/* this will start the transmit */
	rxLast->flag &= ~DMA_FULL_MASK;	    /* this will start the receive */
	
	
 
	RESTORE_INTS;
}
   
/*
 * This routine setup the serial port as SPI slave
 *
 *  Parameters:
 *		port		spi slave port number, 0, 1, 2, or 3
 *
 *  Return Values:
 *		none
 *	   
 */		
static void setupSpiPort (int port)
{

    /*
     * setup serial control register A
     */
    /* spiSlavePorts[port].spiRegs->ctrA = 0; */
    narm_write_reg (NARM_SCRA_REG, &spiSlavePorts[port].spiRegs->ctrA, ce, 0);       /*disable the port */
    narm_write_reg (NARM_SCRA_REG, &spiSlavePorts[port].spiRegs->ctrA, wls, 3);      /*word length of 8 data bits */
    narm_write_reg (NARM_SCRA_REG, &spiSlavePorts[port].spiRegs->ctrA, rl, 0);       /*disable remote loop back */
    narm_write_reg (NARM_SCRA_REG, &spiSlavePorts[port].spiRegs->ctrA, ll, 0);       /*disable remote loop back */
    narm_write_reg (NARM_SCRA_REG, &spiSlavePorts[port].spiRegs->ctrA, erxdma, 1);   /*enable receive DMA */
    narm_write_reg (NARM_SCRA_REG, &spiSlavePorts[port].spiRegs->ctrA, etxdma, 1);   /*enable transmit DMA */
    /*
     * setup serial control register B
     */
    /* spiSlavePorts[port].spiRegs->ctrB = 0; */
    narm_write_reg (NARM_SCRB_REG, &spiSlavePorts[port].spiRegs->ctrB, mode, 3);     /*SPI slave mode */
    narm_write_reg (NARM_SCRB_REG, &spiSlavePorts[port].spiRegs->ctrB, bitordr, spiSlavePorts[port].bitOrder);        /*bit order */
    narm_write_reg (NARM_SCRB_REG, &spiSlavePorts[port].spiRegs->ctrB, cspol, 0); /*the SPI slave is fixed to an active low chip select */


    /*
     * setup serial bit-rate register
     */
	/*
	 * the 'N' value is not required for
     * Spi Slave mode; the design just samples the edges of the
     * incoming block to get the proper bit rate.  but EBIT in 
	 * the Bit-Rate register still needs to be 
     * asserted so that RXINV, TXINV bits can still be used to
     * specify the proper SPI Clock mode for sampling/driving edges.
     */
   /* spiSlavePorts[port].spiRegs->brg = 0; */
    narm_write_reg (NARM_SBRG_REG, &spiSlavePorts[port].spiRegs->brg, ebit, 1);      /*bit-rate generator enable */
    narm_write_reg (NARM_SBRG_REG, &spiSlavePorts[port].spiRegs->brg, tmode, 1);     /*timing mode must be 1 */
    narm_write_reg (NARM_SBRG_REG, &spiSlavePorts[port].spiRegs->brg, rxsrc, 1);     /*internal timing source */
    narm_write_reg (NARM_SBRG_REG, &spiSlavePorts[port].spiRegs->brg, txsrc, 1);     /*internal timing source */
    narm_write_reg (NARM_SBRG_REG, &spiSlavePorts[port].spiRegs->brg, rxext, 0);     /*disable driver receive clock external */
    narm_write_reg (NARM_SBRG_REG, &spiSlavePorts[port].spiRegs->brg, txext, 0);     /*disable transmitter clock to be driven on GPIO pin */

	narm_write_reg (NARM_SBRG_REG, &spiSlavePorts[port].spiRegs->brg, clkmux, 1);    /*using BCLK for arm9 */
//	narm_write_reg (NARM_SBRG_REG, &spiSlavePorts[port].spiRegs->brg, clkmux, 2);    /*using BCLK for arm9 */
//	narm_write_reg (NARM_SBRG_REG, &spiSlavePorts[port].spiRegs->brg, clkmux, 3);    /*using BCLK for arm9 */

    if ( spiSlavePorts[port].spiMode == NA_SPI_MODE0 ||  spiSlavePorts[port].spiMode == NA_SPI_MODE3)
    {
        narm_write_reg (NARM_SBRG_REG, &spiSlavePorts[port].spiRegs->brg, txcinv, 0);        /*transmit on clock falling edge */
        narm_write_reg (NARM_SBRG_REG, &spiSlavePorts[port].spiRegs->brg, rxcinv, 0);
    }
    else
    {
        narm_write_reg (NARM_SBRG_REG, &spiSlavePorts[port].spiRegs->brg, txcinv, 1);        /*transmit on clock risinging edge */
        narm_write_reg (NARM_SBRG_REG, &spiSlavePorts[port].spiRegs->brg, rxcinv, 1);
    }

    if ( spiSlavePorts[port].spiMode == NA_SPI_MODE1 ||  spiSlavePorts[port].spiMode == NA_SPI_MODE3)
    {
        narm_write_reg (NARM_SBRG_REG, &spiSlavePorts[port].spiRegs->brg, spcpol, 0);        /*clock idle high */
    }
    else
    {
        narm_write_reg (NARM_SBRG_REG, &spiSlavePorts[port].spiRegs->brg, spcpol, 1);        /*clock idle low */
    }


}
/*
 * This routine initializes the rx and tx DMA buffer 
 * descriptors, and setup DMA control registers.
 *
 *  Parameters:
 *		port		spi slave port number, 0, 1, 2, or 3
 *
 *  Return Values:
 *		none
 *	   
 */		
static void initDma (int port)
{

    physical_address_t phys;
    
    memset (spiSlavePorts[port].tx_buffer_descriptors, 0, sizeof (txc_buffer_descriptors[port]));
    memset (spiSlavePorts[port].rx_buffer_descriptors, 0, sizeof (rxc_buffer_descriptors[port]));

     
   spiSlavePorts[port].tx_buffer_descriptors->src_addr = 0;
   spiSlavePorts[port].rx_buffer_descriptors->src_addr = 0;
   spiSlavePorts[port].tx_buffer_descriptors->dst_addr = 0;
   spiSlavePorts[port].rx_buffer_descriptors->dst_addr = 0;

   spiSlavePorts[port].tx_buffer_descriptors->buf_len = 0;
   spiSlavePorts[port].rx_buffer_descriptors->buf_len = 0;

   /*don't turn the FULL bit on tx channel, so dma does not attempt to empty tx buffer*/
   spiSlavePorts[port].tx_buffer_descriptors->flag = DMA_WRAP_MASK | DMA_LAST_MASK; 

   /* turn the FULL bit on rx channel, so dma does not attempt to fill rx buffer*/
   spiSlavePorts[port].rx_buffer_descriptors->flag = DMA_FULL_MASK | DMA_LAST_MASK | DMA_WRAP_MASK ;

    /* 
     * setup DMA control register
     */
    spiSlavePorts[port].dmaRegs->dmarcr = 0;
    spiSlavePorts[port].dmaRegs->dmatcr = 0;
    narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmarcr, ce, 0);        /*disable DMA operation */
    narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmatcr, ce, 0);        /*disable DMA operation */

    narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmarcr, ca, 0);        /*disable DMA channel abort */
    narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmatcr, ca, 0);        /*disable DMA channel abort */

    narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmarcr, bb, 0);        /*always set to 0 */
    narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmatcr, bb, 0);        /*always set to 0 */

    narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmarcr, mode, 0);      /*peripheral-to-memory */
    narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmatcr, mode, 1);      /*memory-to-peripheral */

    narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmarcr, bte, 2);       /*burst trsfer enable 4 operands */
    narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmatcr, bte, 2);       /*burst trsfer enable 4 operands */

    /* 
     * setup DMA status/interrupt register
     */
#ifdef NS9360
    narm_write_reg (NA_DMA_DSIER, &spiSlavePorts[port].dmaRegs->dmarsr, ncie, 0);    /*disable ncip interrupt, use NRIP */
    narm_write_reg (NA_DMA_DSIER, &spiSlavePorts[port].dmaRegs->dmatsr, ncie, 0);    /*disable ncip interrupt */
#else
	/*for ns9750, we have to use NCIP*/
	narm_write_reg (NA_DMA_DSIER, &spiSlavePorts[port].dmaRegs->dmarsr, ncie, 1);    /*use NCIP for RX */
    narm_write_reg (NA_DMA_DSIER, &spiSlavePorts[port].dmaRegs->dmatsr, ncie, 0);    /*disable ncip interrupt */
#endif

    narm_write_reg (NA_DMA_DSIER, &spiSlavePorts[port].dmaRegs->dmarsr, nrie, 1);    /*enable nrip interrupt for RX*/
    narm_write_reg (NA_DMA_DSIER, &spiSlavePorts[port].dmaRegs->dmatsr, nrie, 0);    /*disable nrip interrupt */

	narm_write_reg (NA_DMA_DSIER, &spiSlavePorts[port].dmaRegs->dmarsr, ecie, 1);    /*enable ecip interrupt */
    narm_write_reg (NA_DMA_DSIER, &spiSlavePorts[port].dmaRegs->dmatsr, ecie, 1);    /*enable ecip interrupt */

    narm_write_reg (NA_DMA_DSIER, &spiSlavePorts[port].dmaRegs->dmarsr, caie, 0);    /*not enable caip interrupt */
    narm_write_reg (NA_DMA_DSIER, &spiSlavePorts[port].dmaRegs->dmatsr, caie, 1);    /*enable caip interrupt */
 

    /*
     * setup DMA buffer descriptor pointer
     */
    if (!NAVaToPhys ((const void *) spiSlavePorts[port].rx_buffer_descriptors, &phys))
        netosFatalError ("spi tx DMA Cache error", 1, 1);
    spiSlavePorts[port].dmaRegs->dmarbdp = (unsigned long) phys;

    if (!NAVaToPhys ((const void *) spiSlavePorts[port].tx_buffer_descriptors, &phys))
        netosFatalError ("spi tx DMA Cache error", 1, 1);
    spiSlavePorts[port].dmaRegs->dmatbdp = (unsigned long) phys;

}


 
/*
 *	This routine enables spi slave port and dma channel
 *
 *  Parameters:
 *		port		spi slave port number, 0, 1, 2, or 3
 *
 *  Return Values:
 *		none
 *	   
 */		
static void enablePort (int port)
{

     

    /*
     * Enable interrupt
     */
    NAEnableIsr (spiSlavePorts[port].rx_int_id);
 

    /*
     * Enable serial port
     */
    narm_write_reg (NARM_SCRA_REG, &spiSlavePorts[port].spiRegs->ctrA, ce, 1);       /*enable the port */

    /*
     * Enable dma
     */
    narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmarcr, ce, 1);        /*enable DMA operation */
    narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmatcr, ce, 1);        /*enable DMA operation */

}

/*
 *	This routine disables spi slave port and dma channel
 *
 *  Parameters:
 *		port		spi slave port number, 0, 1, 2, or 3
 *
 *  Return Values:
 *		none
 *	   
 */		
static void disablePort (int port)
{
    /*
     * disable interrupt
     */
    NADisableIsr (spiSlavePorts[port].rx_int_id);


    /*
     * Disable dma
     */
    narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmarcr, ce, 0);
    narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmatcr, ce, 0);

    /*
     * Disable serial port
     */
    narm_write_reg (NARM_SCRA_REG, &spiSlavePorts[port].spiRegs->ctrA, ce, 0);

     

}

/*
 *	This routine is rx isr
 *
 *  Parameters:
 *		port		spi slave port number, 0, 1, 2, or 3
 *
 *  Return Values:
 *		none
 *	   
 */		 
#if 0
unsigned gIsrRxStatus[100] = {0};
unsigned gIsrTxStatus[100] = {0};
unsigned gIsrStatusCount = 0;
#endif

static void spi_slave_rx_dma_int (int port)
{
 
#ifdef NS9360
#define RECEIVE_MASK  DMA_NRIP_MASK
#else
#define RECEIVE_MASK  DMA_NCIP_MASK 	/*for ns9750, we have to use NCIP*/
#endif

	 INTERRUPT_SAVE_AREA;
    unsigned long rx_status = spiSlavePorts[port].dmaRegs->dmarsr;
	unsigned long tx_status = spiSlavePorts[port].dmaRegs->dmatsr;
	SPISLAVE_BUFFER_T *pBuffer = NULL, *nextBuffer;
	unsigned long virtualBuffer;

    /* Acknowledge interrupt */
    spiSlavePorts[port].dmaRegs->dmarsr |= rx_status;
	spiSlavePorts[port].dmaRegs->dmatsr |= tx_status;

#if 0
    gIsrRxStatus[gIsrStatusCount] = rx_status;
    gIsrTxStatus[gIsrStatusCount] = tx_status;

    gIsrStatusCount++;
    if (gIsrStatusCount == 100) gIsrStatusCount=0;
#endif
    if (rx_status & DMA_ECIP_MASK)
    {
        netosFatalError ("spi rx DMA error", 1, 1);
    }

	if (tx_status & DMA_ECIP_MASK)
    {
        netosFatalError ("spi tx DMA error", 1, 1);
    }

	/* when the receive is done, the transmit should be done too */
    if ((rx_status & RECEIVE_MASK) == RECEIVE_MASK) 
    {
		spiSlavePorts[port].rx_int_cnt++;


		/* reset the slave port in case there is any data in TX FIFO */
		spiSlaveReset(port);


		/* Then abort TX DMA channel in case the TXed data length < TX buffer length. TX DMA ce=1, ca=1 */
		narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmatcr, ca, 1);
		narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmatcr, ce, 1); 

		/* The CAIP bit should set. read the status register and write back to clear it, then disable abort */
		tx_status = spiSlavePorts[port].dmaRegs->dmatsr;
		spiSlavePorts[port].dmaRegs->dmatsr |= tx_status;
		narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmatcr, ca, 0);


		/* take out the buffer from the head of the Q */
		DISABLE_INTS;
		 pBuffer = spiSlaveDeque(&spiSlavePorts[port].bufferQ);
		RESTORE_INTS;
		 if (pBuffer == NULL)
		 {
			netosFatalError ("spi rx DMA error", 1, 1);
		 }
		 else
		 {
			 if ((spiSlavePorts[port].rx_buffer_descriptors->src_addr != (WORD32)pBuffer->buffer->readBuffer) || 
					(spiSlavePorts[port].tx_buffer_descriptors->src_addr != (WORD32)pBuffer->buffer->writeBuffer))
			 {
					netosFatalError ("spi tx DMA error", 1, 1);
			 }
			 else
			 {
#ifdef NS9360
				 /* update the actual length field before pass it to call back function */
				pBuffer->buffer->byteReceived = spiSlavePorts[port].rx_buffer_descriptors->buf_len;
#else
				pBuffer->buffer->byteReceived = pBuffer->buffer->bufferLength - spiSlavePorts[port].rx_buffer_descriptors->buf_len;
#endif

				/*
				 * Convert the physical address back to virtual address
				 */
 
				virtualBuffer = tx_assoc[port];
				NA_ASSERT (virtualBuffer);
//				NAAfterDMA (NA_MEMORY_TO_DEVICE_DMA, NULL, (const void *) virtualBuffer, pBuffer->buffer->bufferLength);
				NAAfterDMA (NA_MEMORY_TO_DEVICE_DMA, (const void *) virtualBuffer, NULL, pBuffer->buffer->bufferLength);

				virtualBuffer = rx_assoc[port];
				NA_ASSERT (virtualBuffer);
				NAAfterDMA (NA_DEVICE_TO_MEMORY_DMA, NULL, (const void *) virtualBuffer, pBuffer->buffer->bufferLength);
 

				 /*call user callback functions to process the received and transmitted buffer*/
				 (pBuffer->buffer->bufferReturnFn)(pBuffer->buffer);

			 }

			/* is there any more buffers in the queue? */
			DISABLE_INTS;
			if (spiSlavePorts[port].bufferQ.head)
			{
				nextBuffer = spiSlavePorts[port].bufferQ.head;
				setupDma (port, nextBuffer->buffer->writeBuffer, nextBuffer->buffer->readBuffer, nextBuffer->buffer->bufferLength);
				spiSlavePorts[port].dmaInuse = 1;
			}
			else
			{
				spiSlavePorts[port].dmaInuse = 0;
			}
			RESTORE_INTS;


			 /* free the local buffer */
			 freePB (pBuffer);
             
		 } 
    }
	else
	{
		spiSlavePorts[port].unknown_int_cnt++;
	}

	/*
     * Enable dma
     */
	DISABLE_INTS;
	if (spiSlavePorts[port].dmaInuse == 1)
	{
		narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmarcr, ce, 1);        /*enable DMA operation */
		narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmatcr, ce, 1);        /*enable DMA operation */
	}
	RESTORE_INTS;

}

 
/*
 * This routine resets the spi slave channel
 * and it is called from recv DMA interrupt.
 */
static void spiSlaveReset(int port)
{

	/* disable TX slave DMA */
	narm_write_reg (NA_DMA_DCR, &spiSlavePorts[port].dmaRegs->dmatcr, ce, 0); 

	/* reset slave serial port, so that the FIFO is flushed out */
	switch (port)
	{
	case 0:
		narm_write_reg (NA_BUM_MRR_REG, NA_BUM_MRR_ADDRESS, ser2, 1); 
		narm_write_reg (NA_BUM_MRR_REG, NA_BUM_MRR_ADDRESS, ser2, 0);
		break;

	case 1:
		narm_write_reg (NA_BUM_MRR_REG, NA_BUM_MRR_ADDRESS, ser1, 1); 
		narm_write_reg (NA_BUM_MRR_REG, NA_BUM_MRR_ADDRESS, ser1, 0);
		break;

	case 2:
		narm_write_reg (NA_BUM_MRR_REG, NA_BUM_MRR_ADDRESS, ser3, 1); 
		narm_write_reg (NA_BUM_MRR_REG, NA_BUM_MRR_ADDRESS, ser3, 0);
		break;

	case 3:
		narm_write_reg (NA_BUM_MRR_REG, NA_BUM_MRR_ADDRESS, ser4, 1); 
		narm_write_reg (NA_BUM_MRR_REG, NA_BUM_MRR_ADDRESS, ser4, 0);
		break;

	default:
		break;
	}

	/* reconfigure serial channel and enable it */
	setupSpiPort (port);
	narm_write_reg (NARM_SCRA_REG, &spiSlavePorts[port].spiRegs->ctrA, ce, 1);       

	
}


/*
 *	This routine initialize the spi slave buffer queue.
 *
 *  Parameters:
 *		pQ			spi slave buffer queue pointer
 *		size		the max queue size
 *
 *  Return Values:
 *		none
 *	   
 */		 
static void spiSlaveInitQ(SPISLAVE_BUFFER_Q * pQ, int size)
{
	 pQ->head = NULL;
	 pQ->tail =	pQ->head;
	 pQ->len= 0;
	 pQ->max = size;
	 
}

/*
 *	This routine adds a buffer to the end of the buffer queue.
 *
 *  Parameters:
 *		pQ			spi slave buffer queue pointer
 *		pBuffer		spi slave buffer pointer
 *
 *  Return Values:
 *		QSUCCESS    success
 *      QFAIL       queue is full
 *	   
 */			
static int spiSlaveEnque(SPISLAVE_BUFFER_Q * pQ, SPISLAVE_BUFFER_T *pBuffer)
{
    
    pBuffer->next = NULL;

	if (pQ->head == NULL)
	{
		pQ->head = pQ->tail = pBuffer;
	}
	else
	{ 
		if (pQ->len < pQ->max)
		{
			pQ->tail->next = pBuffer;
			pQ->tail = pBuffer;
		}
		else
		{
			return QFAIL;
		}
	}
	pQ->len++;
	return QSUCCESS;
}
 
/*
 *	This routine removes and returns the head buffer from the queue 
 *
 *  Parameters:
 *		pQ			spi slave buffer queue pointer
 *
 *  Return Values:
 *		spi slave buffer pointer
 *	   
 */		
static SPISLAVE_BUFFER_T * spiSlaveDeque(SPISLAVE_BUFFER_Q * pQ)
{
	SPISLAVE_BUFFER_T *pBuffer = NULL;
	
	if (pQ->head)
	{
		pBuffer = pQ->head; 
		pQ->head = pBuffer->next;
		pBuffer->next = NULL;
		pQ->len--;
	}

	if (pQ->len == 0)
		pQ->head = NULL;


	return pBuffer;
}


/*
 *	This routine allocates a queue buffer for "enqueuing" to avoid
 *  calling malloc() in isr.
 *
 *  Parameters:
 *		none
 *
 *  Return Values:
 *		spi slave buffer pointer
 *	   
 */	
static SPISLAVE_BUFFER_T * mallocPB (void)
{
	SPISLAVE_BUFFER_T *pBuffer=NULL;
	int i;
	INTERRUPT_SAVE_AREA;


	DISABLE_INTS;
	for (i=0; i< MAX_Q_SIZE; i++)
	{
		if (pBufferTable[i].inuse == 0)
		{
			pBufferTable[i].inuse = 1;
			pBuffer = &pBufferTable[i].pBuffer;
			break;
		}
	}

	RESTORE_INTS;

	return pBuffer;

}


/*
 *	This routine frees a queue buffer for "dequeuing" to avoid
 *  calling free() in isr.
 *
 *  Parameters:
 *		spi slave buffer pointer
 *
 *  Return Values:
 *		None
 *	   
 */	
static void freePB (SPISLAVE_BUFFER_T *pBuffer)
{
	int i;
	INTERRUPT_SAVE_AREA;


	DISABLE_INTS;
	for (i=0; i< MAX_Q_SIZE; i++)
	{
		if ((pBufferTable[i].inuse == 1) && (&pBufferTable[i].pBuffer == pBuffer))
		{
			pBufferTable[i].inuse = 0;
			memset(&pBufferTable[i].pBuffer, 0, sizeof pBufferTable[i].pBuffer);
			break;
		}
	}

	RESTORE_INTS;

}
