/*
 *            Copyright (c) 1998-2004 by NETsilicon Inc.
 *
 *  This software is copyrighted by and is the sole property of
 *  NETsilicon.  All rights, title, ownership, or other interests
 *  in the software remain the property of NETsilicon.  This
 *  software may only be used in accordance with the corresponding
 *  license agreement.  Any unauthorized use, duplication, transmission,
 *  distribution, or disclosure of this software is expressly forbidden.
 *
 *  This Copyright notice may not be removed or modified without prior
 *  written consent of NETsilicon.
 *
 *  NETsilicon, reserves the right to modify this software
 *  without notice.
 *
 *  NETsilicon
 *  411 Waverley Oaks Road                  USA 781.647.1234
 *  Suite 227                               http://www.netsilicon.com
 *  Waltham, MA 02452                       AmericaSales@netsilicon.com
 *
 *************************************************************************
 
 *     Module Name: netos_spi.c
 *         Version: 1.00
 *   Original Date: 03/27/01
 *          Author: Naum Gurevich
 *          Based : upon seril driver by Shing Wu
 *        Language: Ansi C
 * Compile Options:
 * Compile defines:
 *       Libraries:
 *    Link Options:
 *
 *    Entry Points:
 *
 * Description.
 * =======================================================================
 *
 *
 * Edit Date/Ver   Edit Description
 * ==============  ===================================================
 *
 *	01/13/02 DXL	- turn off the transmitter clock off for slave mode
 *  04/16/02 DXL	- Cleaned up variable declared but never reference; 
 *						warnings generated by new compiler
 *					- Changed from ptr_dmareg->stat_reg != DMA_CLAER_ALL_INTERRUPTS to |=
 *				
 *  05/16/03 JZW    - Replaced all c++-style comments with c-style comments. This
 *                    eliminates compiler warnings in GNU gcc
 */

#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <tx_port.h>
#include <semaphore.h>
#include <ddi.h>
#include <netosIo.h>
#include <reg_def.h>

#include <serParam.h>

#include "netos_spi.h"
#include <NA_ISR.h>
#include <Dma_def.h>
#include <sysClock.h>
#include <reg_def.h>
#include <bool.h>
#include <tx_api.h>
#include <ncc_init.h>
#include "spi_mod.h"

/* #define EEPROM_SUPPORT   1 */

/* data buffers */
volatile SPIBUFFER spiDataBuffer[NUM_NCC_SER_CHAN];
volatile SPIBUFFER spiInBuffer[NUM_NCC_SER_CHAN];

/* Local data structures */

int chipRevision;

#ifdef DEBUG_SPI
     static void printReg(void);
#endif
 

#ifdef debug_spi

   WORD32 statreg[42];
   int intCount;
 
   WORD32 reg10st, reg10cr;
   WORD32 reg9st[3], reg9cr[3];
   int intCount;
    
   WORD32 tx_upper_word[2], tx_lower_word[2];
   WORD32 rx_upper_word[2], rx_lower_word[2];
#endif

/**********************************************************************************
 **********************************************************************************
 ***                                                                            ***
 *** Function Name:  get_baud_div()                                             ***
 ***                                                                            ***
 *** Called by:      onspiInitChannel(), onspiIoctl()                           ***
 ***                                                                            ***
 *** Comments: This routine returns the baud rate divisor which is required     ***
 *** to set up the hardware for the desired baudrate. 
 ***                                                                            ***
 *** This function calculates the optimum N value (Divr based on the values     ***
 *** of SysClk or Xtal Frequency.                                               ***
  *** The high-speed values for DMA mode are calculated based upon an 18.432
 *** MHz crystal and a system PLL multiplier of 5,
 *** then the SYS-CLK=29.491 MHz
 *** (see Section 7.5.4 of the NET+Works Hardware Reference Guide).
 *** If the SYS_CLK value changes, the actual speed has to be calculated
 *** using the equation provided in the Hardware Reference Guide,
 *** Section 7.5.4, Serial Bit-Rate Registers).
 *** Because this is a synchronies protocol, exact timing is less significant
 *** then on the RS-232 protocol. If exact baud rate is unavailable,
 *** the closest available rate will be used.
 **********************************************************************************
 **********************************************************************************/

int get_baud_div (int baudrate)
{
    unsigned int Nsysclk, Nxtal;
    unsigned int Esysclk, Extal;
    unsigned int sysClkFreq, xtalFreq;

    UINT32 divr;

    sysClkFreq = NAgetSysClkFreq();
    xtalFreq = NAgetXtalFreq();
    
    /*  compute the N value for sysclk  */
    Nsysclk = (sysClkFreq / (2 * baudrate));

    /*  check to round up   */
    if ((sysClkFreq % (2 * baudrate)) > baudrate)
    {
        /*  the remainder was greater than half the previous denominator    */
        Nsysclk++;
    }

    /*  The n parameter can not be greater than 11 bits of storage  */
    if (Nsysclk > 2047)
    {
        /*  error is 100 %  */
        Esysclk = baudrate;
    }
    else
    {
        /*  compute the delta using the sysclk -- delta should always be positive  */
        if ( (sysClkFreq / (2 * Nsysclk)) > baudrate)
        {
            Esysclk = (sysClkFreq / (2 * Nsysclk)) - baudrate;
        }
        else
        {
            Esysclk = baudrate - (sysClkFreq / (2 * Nsysclk ));
        }

    }

    /*  compute the N value for xtal  */
    if ((xtalFreq / (2 * baudrate)) >= 1)  /* in case baudrate is > 1Mhz */
    {
	    Nxtal = (xtalFreq / (2 * baudrate));
    }
    else
    {
	    Nxtal = 0;   
    }

    /*  check to round up   */
    if (((xtalFreq % (2 * baudrate)) > baudrate) || Nxtal == 0)
    {
        /*  remainder exists or Nxtal equl 0.    */
        Nxtal++;
    }

    /*  The n parameter can not be greater than 11 bits of storage  */
    if (Nxtal > 2047)
    {
        /*  error is 100 %  */
        Extal = baudrate;
    }
    else
    {
        /*  compute the delta using the xtal -- delta should always be positive  */
        if ((xtalFreq / (2 * Nxtal)) > baudrate)
        {
            Extal = (xtalFreq / (2 * Nxtal)) - baudrate;
        }
        else
        {
            Extal = baudrate - (xtalFreq / (2 * (Nxtal + 1)));
        }
    }
#ifdef debug_spi
	printf("@baudrate %d, Esysclk is %d, Extal is %d, ", baudrate, Esysclk, Extal);
#endif
    /*  pick the best scenario  */
    if (Esysclk < Extal)
    {
        /*  sysclk error less than or equal to XTAL clock  */
        divr = BIT_RATE_CLK_SYS | Nsysclk;  
#ifdef debug_spi
		printf("Nsysclk is %d, Esysclk error in percent is %d\n",Nsysclk, (Esysclk * 100) / baudrate);
#endif		
    }
    else
    {
        /*  sysclk error greater than XTAL clock  */
        divr = BIT_RATE_CLK_EXT | Nxtal;
#ifdef debug_spi
        printf("Nxtal is %d, Extal error in percent is %d\n",Nxtal, (Extal * 100) / baudrate);
#endif		
    }

    return divr;
}


void setBitOrder (ONSPI_CHAN * pChan)
{
    UINT32 reg;

    reg = SER_REG (pChan->port, CTRL_REGB);
    if (pChan->LSBfirst)
    {
	    reg &= ~SPI_MSB_FIRST_VALUE;
    }
    else
    {
    	reg |= SPI_MSB_FIRST_VALUE;
    }
}

/*
 * Function: setBitRate()
 *
 * Description: set bit rate registry.
 *              This registry controls baud rate, serial clock phase and polarity. 
 *
 * Called by: onspiIoctl, onspiInitChannel
 *
 * Input : pChan - pointer to the channel description
 *
 * Return: EBADF  
 *         EINVAL
 *         EBUSY
 *         EAGAIN
 *         EXIT_SUCCESS
 *
 */
void setBitRate(ONSPI_CHAN * pChan)
{
    UINT32 divr;
    UINT32 reg = 0;
    
    if (pChan->slaveMode)
    {
	    reg = BIT_GEN_ON | BIT_RATE_X1 | RECV_CLK_SRC_EXT | XMIT_CLK_SRC_EXT |
              RECV_CLK_OUT_OFF | XMIT_CLK_OUT_OFF ;
    }
    else
    {
	    reg = BIT_GEN_ON | BIT_RATE_X1 | RECV_CLK_SRC_INT | XMIT_CLK_SRC_INT |
              RECV_CLK_OUT_ON | XMIT_CLK_OUT_ON ;
    }

    /* set Clock Invert value */
    if (pChan->startClockHigh)
    {
	    reg |= BIT_RATE_CLKINV;
    }
    else
    {
	    reg &= ~BIT_RATE_CLKINV;
    }
     
     /* set edge of TX clock  */
    if (pChan->TXCINVset)
    {
	    reg |= BIT_RATE_TXCINV;
    }
    else
    {
	    reg &= ~BIT_RATE_TXCINV;
    }
     
     /* set edge of RX clock */
    if (pChan->RXCINVset)
    {  
	    reg |= BIT_RATE_RXCINV;
    }
    else
    {
	    reg &= ~BIT_RATE_RXCINV; 
    }

	 
    divr = get_baud_div (pChan->baudRate); 
    reg |= divr;

    SER_REG (pChan->port, BAUD_REG) = reg;
}

/*
 * Function: setISRforSEL ()
 *
 * Description: Set port for use interrupt on the SEL signal.
 *              
 *
 * Called by: onspiIoctl
 *
 * Input : pChan - pointer to the channel description
 *         arg - port number and bit number in the port
 *
 * Return ENOSYS if port is invalid or ISR cannot be installed
 *
 *
 */
 
int setISRforSEL(ONSPI_CHAN* pChan, int * arg)
{
    static int ISRforPORTC[] =
     {
    	 PCPC0_INT,
	     PCPC1_INT,
    	 PCPC2_INT,
	     PCPC3_INT
     };

    int port;
    int bit;
    int status;

    port = (*arg) >> 8;

    bit = (*arg) & 0xFF;

    if (port == 0)
    {
    	if (pChan->ISRlevel != -1)
	    {
	        NAUninstallIsr(pChan->ISRlevel);
    	    pChan->ISRlevel = -1;
	    }

    	return OK; /* We do not need additional SS signal */
    }

    if (port != 3 || bit > 3 || bit < 1)
    {
	    return ENOSYS; /* Only PORT C and bits 1,2, and 3 can be use */
    }

    status = NAInstallIsr (ISRforPORTC[bit], (NA_ISR_HANDLER)onspiISRforSel, (void*) pChan);  /*install interrupt function for SEL */
    if (status != SUCCESS)
    {
    	return ENOSYS; /* can not install ISR routine */
    }

    pChan->ISRbit = 1 << bit;
	/*
     * *NARM_PORTC_ADDR &= ~pChan->ISRbit;
     * *NARM_PORTC_ADDR |= pChan->ISRbit;
	*/
    *NARM_PORTC_ADDR |= (1 << (bit+24));
    *NARM_PORTC_ADDR |= (1 << (bit+16)); /* set special function for low-to-high transition */
    pChan->ISRlevel = ISRforPORTC[bit];
    
    return OK;
}

/*
 * Function: setSSport()
 *
 * Description: Set port for generating "Slave Select" signal
 *
 * Called by: onspiIoctl
 *
 * Input : channel - pointer to the port decription
 *         agr - pointer to the port and bit number
 *           
 *
 * Return: ENOSYS  
 *         OK
 *
 */

int setSSport(ONSPI_CHAN* pChan, int * arg)
{
    static unsigned long volatile * portAssigments[] =
    {
	    NARM_PORTA_ADDR,
    	NARM_PORTB_ADDR,
	    NARM_PORTC_ADDR,
    	NARM_PORTD2_ADDR,
	    NARM_PORTF_ADDR,
    	NARM_PORTG_ADDR,
	    NARM_PORTH_ADDR
    };

    int port;
    int bit;

    port = (*arg) >> 8;

    bit = (*arg) & 0xFF;

    if (port == 0)
    {
    	pChan->SSport = NULL;
	    return OK; /* We do not need additional SS signal */
    }

    if (port > 7 || bit > 7)
    {
    	return ENOSYS; /* we have 6 port and each port can use 7 bits */
    }

    if (chipRevision < 0X19 && port > 3)
    {
	    return ENOSYS; /* NET ARM 40  can use only 3 ports */
    }

    /*
	In order to use registers D - H 1284 (SW4/8) and PCM0 (SW53/5) must be 
	in the OFF position
     */

    if ((narm_read_reg(NARM_GCR_REG, NARM_GCR_ADDR, enimode) !=  CR_INIT_STATEG) &&
	    port > 3)
    {
    	return ENOSYS; /* we can use only 3 ports if ENI is used for something else */
    }

    port--;
    pChan->SSport = (unsigned long *)portAssigments[port];
    pChan->SSsetHiValue = 1 <<bit;
    pChan->SSsetLowMask = ~pChan->SSsetHiValue;

    /* Set mode of operation */

    *(pChan->SSport) &= ~(1 << (bit+24));
    *(pChan->SSport) |= 1 << (bit+16);

    if (pChan->SSport == NARM_PORTD2_ADDR)
    {
	    /* Port D is a special case */
    	*NARM_PORTD1_ADDR &= ~(1 << (bit+24));
	    *NARM_PORTD1_ADDR |= 1 << (bit+16);
    }
    /* Initial value of sel is HI */ 

    *(pChan->SSport) |= pChan->SSsetHiValue;
    
    return OK;
}


/*
 * Function: waitForCompletion
 *
 * Description: Wait for completion of the current IO operation
 *
 * Called by: netos_read, netos_write, sendWriteEnable, sendEEPROMCommand
 *
 * Input : q- pointer to the data buffer
 *         pch - pointer to the line descriptor
 *         numberOfBytes - number of bytes to transfer
 *         
 *
 * Return: true, if operation completed successfully, false otherwise
 *
 */
 
bool waitForCompletion(const SPIBUFFER volatile *q, ONSPI_CHAN  *pch, const int numberOfBytes)
{
    bool ret;
    int status;
    int numberOfTicks;
    int leftOver;     /* Number of bytes in that are be in FIFO. */
    int fifoleft;     /* Max number of bytes that can be on FIFO */
 

    if (pch->slaveMode && numberOfBytes != 0) /* for the master do not wait too long */
	numberOfTicks = pch->slaveTimeOutValue;
 
    else
	numberOfTicks = calcNumOfTicks(numberOfBytes,pch) ;

    if (pch->SSport != NULL && pch->slaveMode)
	*(pch->SSport) &= pch->SSsetLowMask; /* Set SA signal active low */
    status = tx_semaphore_get (&((pch->spi_int_sem)->sem), numberOfTicks); /* All done */
    pch->IOinProgress = false; 

#if 0
	/*
     * PORTC2 used for timing set it LOW
     * *NARM_PORTC_ADDR &= 0xFEFFFFFB;
     * PORTC0 used for timing set it HI
	*/
  
   *NARM_PORTC_ADDR |= 4;
    
#endif
    if (pch->ISRlevel == -1)
    {
	/* We do not have a clean interrupt on the end of transmition*/
	if (!pch->readInProgres)
	{
	    /*
	      Because net arm does not set TXBC (Transmit Buffer Closed Interrupt Pending)
	      We can get interrupt before XMT buffer is closed
	      We need to wait until FIFO is empty.
	      Net arm 40 chips sets TXEMPTY (Transmit FIFO Empty)
	      bit correctly, so we wait for 5 bytes max.
	      For net arm 50 we must wait for 9 bytes max.
	      1 byte is added for the shift buffer
	    */

	    if (pch->openmode & O_DMA)
        {
            /* Maximum size of fifo */
#ifdef USE
		    fifoleft = 20;
#else
		    fifoleft = 32;
#endif
        }
	    else
	    {
		    if (chipRevision < 19)
            {
		        fifoleft = 3;
            }
		    else
            {
		        fifoleft = 6;
            }
        }

#ifdef USE
	    leftOver = (numberOfBytes < fifoleft) ? numberOfBytes : fifoleft; /* calcNumOfTicks will add 2 bytes */
#else
	    leftOver = (numberOfBytes < fifoleft) ? fifoleft : 32; /* calcNumOfTicks will add 2 bytes */
#endif

	    numberOfTicks = calcNumOfTicks(leftOver,pch);
	}
	else
	{
	    if (pch->baudRate < 10000)
        {
		    numberOfTicks = 1 ; /* Wait for CS HI */
        }
	    else
        {
		    numberOfTicks = 0; /* CS will be HI any way */
        }
	}

	tx_thread_sleep(numberOfTicks);
    }
    spiResetChannel (pch,false);
    if (pch->SSport != NULL)
    {        
	   *(pch->SSport) |= pch->SSsetHiValue;
    }

#ifdef debug_spi
    
    reg2 = SER_REG (pch->port, STAT_REG);
      
    for (i = 0; i < intCount;i ++)
    {
	    if ((i % 8) == 0)
        {
	        printf ("\n Byte # %d ", i/2);
        }
    	printf ("Stat reg %X ",statreg[i]);
    }

    printf ("\n ");
    printf("Satus reg = %X\n",reg2);
     
#endif

    tx_semaphore_put (&((pch->spi_int_sem)->sem)); /* Return semaphore */

    if (status != TX_SUCCESS)
    {
#ifdef DEBUG_SPI
        printf("Error from tx_semaphore. Bytes left %d. Len = %d  \n",
	       (q->countServData + q->countForBufferData),numberOfBytes);
#endif
        status = false;
    }
    else
    {
        status = true;
    }

#if DEBUG_SPI
    printReg();
#endif
   
    ret = isBufferEmpty (q);
    return (ret && status);
}


/*
 * Function: isBufferEmpty
 *
 * Description: check if buffer is empty
 *
 * Called by: waitForCompletion
 *
 * Input : q- pointer to the data buffer
 *          
 *         
 *
 * Return: true, if buffer is empty, false otherwise
 *
 */
bool isBufferEmpty (const SPIBUFFER volatile *q)
{
    bool ret;
    TX_INTERRUPT_SAVE_AREA
    TX_DISABLE    

    ret = ((q->countServData + q->countForBufferData)  == 0);

    TX_RESTORE
    return ret;
}


#ifdef EEPROM_SUPPORT
/*
 * Function: spi_enable_write
 *
 * Description: prepare SPI buffer for sending write enable command to EEPROM
 *
 * Called by: sendWriteEnable 
 *
 * Input : q- pointer to the data buffer
 *          
 *         
 */
static void spi_enable_write (SPIBUFFER volatile *q )
{
    q->ptrSerData = (char *)q->serBufferData.bufferData;
    q->serBufferData.bufferData[0] = XICOR_WRITEN ;
    q->countServData = 1;
    q->countForBufferData = 0; 
}
#endif

/*
 * Function: spi_copy_io_buffer
 *
 * Description: copy SPI buffer back to user
 *
 * Called by: sendEEPROMCommand, 
 *
 * Input : q- pointer to the data buffer
 *         c     - pointer to the user buffer
 *         nbytes - number of bytes
 *         pChan - pointer to the line descriptor
 *          
 *         
 */
void spi_copy_io_buffer (SPIBUFFER volatile *q, char *c, int nbytes,ONSPI_CHAN *pChan)
{
#ifdef EEPROM_SUPPORT
    int i;
    int tempBytes;
#endif

    int offset;

    if (pChan->hwConfiguration == SIO_SPI_DIRECT)
    {
		offset = 0;
    }

#ifdef EEPROM_SUPPORT
    else if (pChan->hwConfiguration == SIO_SPI_X25C640U)
    {
		offset = 3;
    }
	else
    {
		offset = 2;
    }
#endif

#ifdef EEPROM_SUPPORT
    if (q->ptrDmaBuffer == NULL)
    {
    	/* We are not using DMA nothing to copy */
#if 0 /* this code can be used when HW will support 2 DMA buffer */
	    q->countServData = offset;
	    tempBytes = (4-((unsigned int)c & 0X000003)) & 0X000003 ;
	    tempBytes = (tempBytes < nbytes)? tempBytes : nbytes;
     
	    for (i = 0; i < tempBytes; i++)
	    {
	        *(c++) = q->serBufferData.bufferData[offset+i];
	    }
#endif
    }
    else
    {
	    fmemcpy(c,(q->ptrDataBuffer + offset),nbytes);
    }
#else
    {
	    fmemcpy(c,(q->ptrDmaBuffer + offset),nbytes);
    }
#endif


}

/*
 * Function: spi_set_io_buffer 
 *
 * Description: copy user buffer to the SPI buffer 
 *
 * Called by: netos_spiread, netos_spiwrite 
 *
 * Input : q- pointer to the data buffer
 *         c     - pointer to the user buffer
 *         nbytes - number of bytes
 *         pChan - pointer to the line descriptor
 *         write - true if buffer is needed for writing
 *
 * Return - pointer to the SPI buffer or NULL pointer
 * 
 */
char * spi_set_io_buffer (SPIBUFFER volatile *q, char *c, int nbytes,ONSPI_CHAN *pChan)
{
#ifdef EEPROM_SUPPORT
    int i;
    int tempBytes;
    int offset;
#endif

    q->ptrDmaBuffer = NULL;
   
#ifdef EEPROM_SUPPORT
    q->ptrSerData = (char *)q->serBufferData.bufferData;

    if (pChan->hwConfiguration == SIO_SPI_X25C640U)
    {
	
		if (pChan->readInProgres)
		{
			q->serBufferData.bufferData[0] = XICOR_READ ;
			q->serBufferData.bufferData[1] = (pChan->readAddr & 0x0000ff00) >> 8;
			q->serBufferData.bufferData[2] = (pChan->readAddr & 0x000000ff);
			/*printf("readAddr is %d\n", pChan->readAddr);*/
		}
		else
		{
			q->serBufferData.bufferData[0] = XICOR_WRITE ;
			q->serBufferData.bufferData[1] = (pChan->writeAddr & 0x0000ff00) >> 8;
			q->serBufferData.bufferData[2] = (pChan->writeAddr & 0x000000ff);
			/*printf("writeAddr is %d\n", pChan->writeAddr);*/
		}
		offset = 3;
    }
	else if (pChan->hwConfiguration == SIO_SPI_X25020)
    {
		if (pChan->readInProgres)
		{
			q->serBufferData.bufferData[0] = XICOR_READ ;
			q->serBufferData.bufferData[1] = (pChan->readAddr & 0x000000ff);
			/*printf("readAddr is %d\n", pChan->readAddr);*/
		}
		else
		{
			q->serBufferData.bufferData[0] = XICOR_WRITE ;
			q->serBufferData.bufferData[1] = (pChan->writeAddr & 0x000000ff);
			/*printf("writeAddr is %d\n", pChan->writeAddr);*/
		}
		offset = 2;
    }
    else
    {
    	offset = 0;    
    }

    q->countServData = offset;
#endif

	
#ifdef EEPROM_SUPPORT
    tempBytes = (4 - ((unsigned int)c & 0X000003)) & 0X000003;

    q->ptrDataBuffer = c;
    q->countForBufferData = nbytes;
	/*printf("ptrDataBuffer is 0x%x, countForBufferData is %d\n", q->ptrDataBuffer, q->countForBufferData);*/

    if (pChan->openmode & O_DMA)
    {

#if 0  /* this code can be used when HW will support 2 DMA buffer */
    	tempBytes = (tempBytes < nbytes)? tempBytes : nbytes;
    	q->countForBufferData = (nbytes - tempBytes);
    	q->countServData += tempBytes;

    	for (i = 0; i < tempBytes; i++)
    	{
    	    q->serBufferData.bufferData[offset+i] = *(c++);
    	    q->ptrDataBuffer++;
        }
#endif /* 0 */

	    if (q->countServData == 0 && tempBytes == 0)
	    { /* We need only one DMA buffer */
	        q->countServData = q->countForBufferData;
	        q->countForBufferData = 0;
	        q->ptrSerData = q->ptrDataBuffer;
	    }
	    else  /* we need to copy data */
	    {
	        q->ptrDmaBuffer = malloc(nbytes + q->countServData);
	        if (q->ptrDmaBuffer == NULL)
            {
		        return NULL;
            }

	        fmemcpy((q->ptrDmaBuffer),q->ptrSerData,q->countServData);

	        if (!pChan->readInProgres)
            {
		        fmemcpy((q->ptrDmaBuffer + q->countServData),c,nbytes);
            }

	        q->ptrSerData = q->ptrDmaBuffer;
	        q->ptrDataBuffer = q->ptrDmaBuffer;
	        q->countServData = nbytes+q->countServData;
	        q->countForBufferData = 0;
	    }
    }

    return q->ptrSerData;

#else /* EEPROM_SUPPORT */
    {
	    q->ptrDmaBuffer = malloc(nbytes);
	    if (q->ptrDmaBuffer == NULL)
        {
	        return NULL;
        }

	    if (!pChan->readInProgres)
        {
            fmemcpy(q->ptrDmaBuffer, c, nbytes);
        }

        q->ptrDataBuffer = q->ptrDmaBuffer;
        q->countForBufferData = nbytes;
    }

    return (q->ptrDataBuffer);

#endif /* EEPROM_SUPPORT */

}
 
/*
 * Function: calcNumOfTicks()
 *
 * Description: This routine calculates number of ticks needed to transmit (received) data.
 *
 * Called by: waitForCompletion
 *
 * Input :  length - size of data
 *          pch - pointer to the ONSPI_CHAN structure that has all port related data.
 *
 * Return: number of ticks
 *
 * Comments:  This routine calculates number of ticks needed to transmit (received) data.
 *            It adds 2 ticks because some time time server ignored 1 tick.
 *            (with 1 tick it will wait less then expected 10 ms).
*/
int calcNumOfTicks(const int length, const ONSPI_CHAN  *pch)
{
    
    int totalTime; /*mcsec */
    int numberOfTicks;
    
     
    totalTime = ((8 * 10000 * (length+2))/pch->baudRate);

    numberOfTicks = (totalTime * NABspTicksPerSecond)/10000 + 2;

    return numberOfTicks;
}

#ifdef DEBUG_SPI
static void printReg(void)
{

    int i;

  
    for (i = 0; i < intCount; i++)
     printf(" After inter reg9st = %X , reg9cr %X \n",reg9st[i], reg9cr[i]);
      printf(" After interrupt rx_upper_word[0] = %X, rx_lower_word[0] = %X\n",
	   rx_upper_word[0],
	   rx_lower_word[0] );
   printf(" After interrupt rx_upper_word[1] = %X, rx_lower_word[1] = %X\n",
	   rx_upper_word[1],
	   rx_lower_word[1] );
   
    printf("After interrupt reg10st = %X ,reg10cr %X \n",reg10st, reg10cr);
   printf(" After interrupt tx_upper_word[0] = %X, tx_lower_word[0] = %X\n",

	   tx_upper_word[0],
	   tx_lower_word[0]);

   printf(" After interrupt tx_upper_word[1] = %X, tx_lower_word[1] = %X\n",
	   tx_upper_word[1],
	   tx_lower_word[1] );
}
#endif

/**********************************************************************************
 **********************************************************************************
 ***                                                                            ***
 *** Function Name:  onspiIoctl()                                               ***
 ***                                                                            ***
 *** Called by:                                                                 ***
 ***                                                                            ***
 *** Comments: This routine handles the IOCTL messages from the user.           ***
 *** RETURNS: OK on success, EIO on device error, ENOSYS on unsupported         ***
 *** request.                                                                   ***
 ***                                                                            ***
 *********************************************************************************
 **********************************************************************************/

STATUS onspiIoctl
(
   ONSPI_CHAN * pChan,  /* ptr to ONSPI_CHAN describing this channel */
   int request,          /* request code */
   int* arg               /* some argument */
)
{
    STATUS status;           /* status to return */
#ifdef BSP_ARM9
    UINT32 reg;   /* Temp value for the baud rate register */
#endif

     
    status = ENOSYS;

    switch (request)
    {
    	case SIO_BAUD_SET:
	        pChan->baudRate = *arg;
	        setBitRate(pChan);
    	    status = OK;
	        break;


    	case SIO_BAUD_GET:
	        status = OK;
	        *arg = pChan->baudRate;
    	    break;

    	case SIO_MODE_GET:
	        status = OK;
	        *arg = pChan->openmode;
    	    break;
	    
#ifdef EEPROM_SUPPORT
	    case SIO_GET_REG:
    	case SIO_SET_REG:
	        status = sendEEPROMCommand(pChan,request,arg);
	        break;
#endif
	    
    	case SIO_SLAVE_GET:
	        status = OK;
	        *arg = pChan->slaveMode;
    	    break;

#ifdef EEPROM_SUPPORT	    
	    case SIO_RD_ADDRESS_SET:
	        status = OK;
    	    pChan->readAddr = *arg;
	        break;
	    
    	case SIO_RD_ADDRESS_GET:
	        status = OK;
	        *arg =pChan->readAddr;
    	    break;
	    
    	case SIO_WR_ADDRESS_SET :
	        status = OK;
	        pChan->writeAddr = *arg;
    	    break;

	    case SIO_WR_ADDRESS_GET:
	        status = OK;
    	    *arg =pChan->writeAddr;
	        break;
#endif

    	case SIO_SPI_HW_GET:
	        status = OK;
	        *arg = pChan->hwConfiguration;
    	    break;

	    case SIO_SPI_HW_SET:
	        switch (*arg)
    	    {
	        	case SIO_SPI_DIRECT:
#ifdef EEPROM_SUPPORT
		        case SIO_SPI_X25020:
    		    case SIO_SPI_X25C640U:
#endif    
    	    	    status = OK;
	    	        pChan->hwConfiguration = *arg;
		            break;
    	    }
	        break;
	    
    	case SIO_SPI_SS_SET :
	        if (pChan->slaveMode)
            {
		        break; /* mastrer mode only */
            }
    	    else
            {
	        	status = setSSport(pChan,arg);
            }
    	    break;
	    
    	case SIO_SPI_USE_SEL_ISR :
	        status = setISRforSEL(pChan,arg);
	        break;
	    
    	case SIO_SET_TXCINV:
	        pChan->TXCINVset = *arg;
	        setBitRate(pChan);
    	    status = OK;
	        break;
	    
    	case SIO_SET_RXCINV:
	        pChan->RXCINVset = *arg;
	        setBitRate(pChan);
    	    status = OK;
	        break;
	    
    	case SIO_SET_SPI_CLOCK_HI:
	        pChan->startClockHigh = *arg;
	        setBitRate(pChan);
    	    status = OK;
	        break;

    	case SIO_SET_SA_SIGNAL:
	        if (!pChan->slaveMode)
            {
	    	    break; /* slave mode only */
            }
    	    else
            {
		        status = setSSport(pChan,arg);
            }
    	    break;

    	case SIO_SET_SLAVE_TIMEOUT :
	        if (!pChan->slaveMode)
            {
	    	    break; /* slave mode only */
            }
    	    else
    	    {
	        	pChan->slaveTimeOutValue = *arg;
        		status = OK;
	        }
    	    break;
	    
	    case SIO_GET_SLAVE_TIMEOUT :
	        if (!pChan->slaveMode)
            {
        		break; /* slave mode only */
            }
	        else
    	    {
	        	*arg = pChan->slaveTimeOutValue;
        		status = OK;
	        }
       	    break;

	    case SIO_SET_LSB :
	        status = OK;
	        pChan->LSBfirst = *arg;
            setBitOrder (pChan);
	        break;

    	case SIO_GET_LSB:
	        status = OK;
	        *arg = pChan->LSBfirst;
    	    break;
    }

    return (status);
}

/*
 * Function: sendWriteEnable()
 *
 * Description: send write enable command to the EEPROM
 *
 * Called by: netos_spiwrite, sendEEPROMCommand
 *
 * Input : pChan - Poiner to the cahnnel description
 *
 * Return: EBADF 
 *         EINVAL
 *         EBUSY
 *         EAGAIN
 *         EXIT_SUCCESS
 *
 */
int sendWriteEnable(ONSPI_CHAN *pChan )
{
#ifdef EEPROM_SUPPORT
    int channel;
    int status;
#endif

    if (pChan->hwConfiguration == SIO_SPI_DIRECT)
    {
    	goto done;
    }

#ifdef EEPROM_SUPPORT
    spiResetChannel (pChan,true);
    channel = pChan->port;

    pChan->readInProgres = false;
    spi_enable_write(&spiDataBuffer[channel]);

    if (pChan->openmode & O_DMA)
    {
	    status = spi_init_tx_dma(pChan);
    }
    else
    {
	    status = onspiTxStartup (pChan);
    }

    if (status != EXIT_SUCCESS)
    {
    	return status;
    }
    
    if ( !waitForCompletion(&spiDataBuffer[channel],pChan,0))
    {
	    return EAGAIN;
    }
#endif

done:

    return EXIT_SUCCESS;
}
