/*
 *            Copyright (c) 1998-2000 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
 *  06/24/04 JZW    - References to O_SPI_CLOCK_HI removed. This open option
 *                    does not exist
 */

#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>

/* #define debug_spi */                       /* this can be defined to activate debugging */


/*
 * Serial channel resource lock (used by serial driver & HDLC driver
 */
extern int serl_resource_lock[2];

/*
 * The following routones provide simple resource lock mechanism
 */
extern int GetResource(int *resource, void *owner);
extern int ReleaseResource(int *resource, void *owner);

extern void fmemcpy(void*, void*, int);

#define EXIT_SUCCESS   0

#define OK      EXIT_SUCCESS
#define ERROR       (-1)
#define NUM_DMA_SPI_BUF 2            /* set the dma rx buffer number to 2  - arbitary */

/*
 * Semaphore to block multiple accesses to the SPI routines
*/


#define semtext "_spi"  
static char sem_name[NUM_NCC_SER_CHAN][sizeof(semtext)+2]; /* semaphore uses pointer to this string for the names */

/*
  semaphore for reading and writing
  Unlike Serial RS-232 driver SPI has to read and write at the same time
*/
static sem_t   *spi_sem[NUM_NCC_SER_CHAN];

#define GET_SEMA(x)  if (sem_trywait(x) != TX_SUCCESS) return EBUSY;
#define PUT_SEMA(x)  sem_post(x);

/*
 *   Semaphore to indicate end of read/write operation
*/
#define semintertext "_spiint"
static char sem_intr_name[NUM_NCC_SER_CHAN][sizeof(semintertext)+2]; /* semaphore uses pointer to this string for the names */

/* data buffers */

static volatile SPIBUFFER spiDataBuffer[NUM_NCC_SER_CHAN];
static volatile SPIBUFFER spiInBuffer[NUM_NCC_SER_CHAN];
  
static volatile fb_buffer_desc_t  ser_tx_buffer_des[NUM_NCC_SER_CHAN][NUM_DMA_SPI_BUF];  /* DMA transmission channel descriptor */
static volatile fb_buffer_desc_t  ser_rx_buffer_des[NUM_NCC_SER_CHAN][NUM_DMA_SPI_BUF];  /* DMA receiving channel descriptor */
 

/* Local data structures */

static int chipRevision;

static ONCC_SIO_CHAN_PARAS devParas[] =
{
    {INT_VEC_SERIAL1_RX, SER1RX_INT_LVL,
     INT_VEC_SERIAL1_TX, SER1TX_INT_LVL},
    {INT_VEC_SERIAL2_RX, SER2RX_INT_LVL,
     INT_VEC_SERIAL2_TX, SER2TX_INT_LVL},
};

static ONCC_SIO_CHAN_PARAS devDmaParas[] =
{
    {DMA_VEC_SERIAL1_RX, SER1RX_DMA_LVL,  /* interrupt level for the DMA channels */
     DMA_VEC_SERIAL1_TX, SER1TX_DMA_LVL},
    {DMA_VEC_SERIAL2_RX, SER2RX_DMA_LVL,
     DMA_VEC_SERIAL2_TX, SER2TX_DMA_LVL},
};

static ONSPI_CHAN  onspiChan[NUM_NCC_SER_CHAN];

/*
 * Array of pointers to all serial channels configured in system.
 * It is this array that maps channel pointers
 * to standard device names.
 */
 
static ONSPI_CHAN * sysSPIChanals[] =
{
    &onspiChan[0],
    &onspiChan[1] 
};

/* local functions */

static void onspiInitChannel (ONSPI_CHAN *, bool initSeq);   /* Hardware initialization of the NCC serial channel. */
static void setABCreg (ONSPI_CHAN * pChan);
static void blockABCreg (ONSPI_CHAN * pChan);
static void setCtrRebB (ONSPI_CHAN * pChan);
static void OptSet(const ONSPI_CHAN * pChan );
static void setBitRate(ONSPI_CHAN * pChan);
static void SpiDevInit (ONSPI_CHAN *);	/* initialize the serail driver */
static STATUS onspiIoctl (ONSPI_CHAN * pChan, int request, int* arg);  /* sets driver parameters */
static int sendEEPROMCommand(ONSPI_CHAN *pChan,int request,int * arg);
static void spi_set_command(SPIBUFFER volatile *q,char request,int arg);
static void spi_copy_reply(SPIBUFFER volatile *q,int *arg);
static void spiResetChannel (const ONSPI_CHAN *pChan, const bool resetPort ); /* Set driver operational mode */
static int onspiTxStartup (ONSPI_CHAN* pChan);  /* Call interrupt level character output routine */
 
static int sendWriteEnable(ONSPI_CHAN *pChan );
static int onspiRxStartup (ONSPI_CHAN* pChan); /* Call interrupt level character input routine */
 
static int setSSport(ONSPI_CHAN* pChan, int * arg); /* Set port for generating "Slave Select" or
						       "Salve active" signal */
static int setISRforSEL(ONSPI_CHAN* pChan, int * arg); /* Use interrupt on the SEL signal*/
static int get_baud_div (int baudrate);  /* returns the baud rate divisor  */
 
static int calcNumOfTicks(const int length,const ONSPI_CHAN  *pch);
static void onspiDmaWr (ONSPI_CHAN * pChan);     /* Dma interrupt function when done with send*/
static void onspiDmaRd (ONSPI_CHAN *pChan);        /* Dma interrupt function when done with read*/
static void onspiISRforSel(ONSPI_CHAN * pChan);
static void onspiIntWr40 (ONSPI_CHAN * pChan);  /* interrupt function when ready to send*/
static void onspiIntWr50 (ONSPI_CHAN * pChan);  /* interrupt function when ready to send*/
static void onspiIntRd (ONSPI_CHAN * pChan);    /* interrupt function when ready to read*/
static int spi_init_tx_dma(ONSPI_CHAN  *pch);  /* set up the dma and start it */
static int spi_init_rx_dma(ONSPI_CHAN  *pch);  /* set up the dma and start it */

 
/* wait for the completion of the data transfer */
static bool waitForCompletion(const SPIBUFFER volatile *q, ONSPI_CHAN  *pch, const int numberOfBytes);

/* prototype for data manipulation */
static bool isBufferEmpty (const SPIBUFFER volatile *q);   /* check if buffer is empty */

static char * spi_set_io_buffer (SPIBUFFER volatile *q, char *c, int nbytes,ONSPI_CHAN *pChan);
static void spi_enable_write (SPIBUFFER volatile *q);	
static void spi_copy_io_buffer (SPIBUFFER volatile *q, char *c, int nbytes,ONSPI_CHAN *pChan);

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

/* global device table, defined in ind_os.c */
extern deviceInfo deviceTable[];
unsigned static AllSPIOptins = (O_RDONLY|O_WRONLY|O_RDWR|O_DMA|O_SPI_SLAVE|O_SPI_BLOCKSEL);

#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: spi_init_tx_dma
 *
 *  Description: initialized dma transmit buffer descriptor
 *  and started DMA operation
 *
 *
 *  called by: netos_spiwrite,netos_spiread sendWriteEnable, sendEEPROMCommand
 *
 *  Parameter:
 *
 *      channel      channel number as set in the channel's
 *                   device table entry
 *  Return Values:
 *
 *      EXIT_SUCCESS
 *	EAGAIN          the semaphore was already locked,
 *      EINVAL          'sem' is not a valid semaphore,
 *      ENOSYS          semaphores are not supported,
 *      EINTR           the function was interrupted by a signal,
 *      EDEADLK         a deadlock condition was detected.
 */

static int spi_init_tx_dma(ONSPI_CHAN  *pch)  /* set up the dma for output and start it */
{
     
    int channel;
    SPIBUFFER volatile *q;
    DMA_CTRL_REG_TYPE *ptr_dmareg;
    int status;

    status = sem_trywait(pch->spi_int_sem);
    if (status != TX_SUCCESS)
    {/* something wrong */
	sem_post(pch->spi_int_sem);
	return status;
    }
    if (pch->ISRlevel != -1)
	NAEnableIsr(pch->ISRlevel);
    
    channel = pch->port;
    switch (channel)
    {
        case 0 :
	    ptr_dmareg = DMA_CONF_PTR(DMA8);
	    break;
	case 1:
	     ptr_dmareg = DMA_CONF_PTR(DMA10);
	    break;
    }

    q = &(spiDataBuffer[channel]);
    
    ser_tx_buffer_des[channel][0].lower_word.bits.wrap = 0;
    ser_tx_buffer_des[channel][0].lower_word.bits.intr = 0;
    ser_tx_buffer_des[channel][0].lower_word.bits.last = 0;
    ser_tx_buffer_des[channel][0].lower_word.bits.sbp =  (UINT32)q->ptrSerData;
    ser_tx_buffer_des[channel][0].upper_word.bits.status = 0;
    ser_tx_buffer_des[channel][0].upper_word.bits.full = 1;
    ser_tx_buffer_des[channel][0].upper_word.bits.buflen =
	(q->countServData & DMA_BUF_LEN_MSK);

    ser_tx_buffer_des[channel][1].lower_word.bits.wrap = 1;
    ser_tx_buffer_des[channel][1].lower_word.bits.intr = 0;
    ser_tx_buffer_des[channel][1].lower_word.bits.last = 0;
    ser_tx_buffer_des[channel][1].lower_word.bits.sbp =  (UINT32)q->ptrDataBuffer;
    ser_tx_buffer_des[channel][1].upper_word.bits.status = 0;
    if (q->countForBufferData == 0)
    {
	ser_tx_buffer_des[channel][1].upper_word.bits.full = 0;
	ser_tx_buffer_des[channel][0].lower_word.bits.wrap = 1;
    }
    else
	ser_tx_buffer_des[channel][1].upper_word.bits.full = 1;

    ser_tx_buffer_des[channel][1].upper_word.bits.buflen =
	(q->countForBufferData & DMA_BUF_LEN_MSK);

    if (pch->SSport != NULL && !pch->slaveMode)
	*(pch->SSport) &= pch->SSsetLowMask;
    
    /*now assign buffer descriptor to dma control register*/

    ptr_dmareg->ctrl_reg = 0;
    ptr_dmareg->bd_ptr = (UINT32)  &ser_tx_buffer_des[channel];
    ptr_dmareg->stat_reg =
	  (DMA_CLAER_ALL_INTERRUPTS | DMA_NORM_DONE_IE | DMA_ERR_DONE_IE | DMA_CHAN_ABORT_IE);
    ptr_dmareg->ctrl_reg = ( FLY_BY_READ | DMA_CHANNEL_ENABLE);
     
#if 0  
   printf(" After setup tx_upper_word[0] = %X, tx_lower_word[0] = %X\n",
	   ser_tx_buffer_des[channel][0].upper_word.reg,
	   ser_tx_buffer_des[channel][0].lower_word.reg );
   printf(" After  setup tx_upper_word[1] = %X, tx_lower_word[1] = %X\n",
	   ser_tx_buffer_des[channel][1].upper_word.reg,
	   ser_tx_buffer_des[channel][1].lower_word.reg );
#endif

#ifdef debug_spi
    /* PORTC2 used for timing set it LOW */
    *NARM_PORTC_ADDR &= 0xFEFFFFFB;
    intCount = 0;
#endif
    
    SER_REG(channel, CTRL_REGA) |= XMITR_DMA_ON; /* And start DMA */
    NAEnableIsr (pch->txDmalevel);
    return status;
}

/*
 *  Function: spi_init_rx_dma
 *
 *  Description: initialized dma receive buffer descriptor
 *  and started DMA operation
 *
 *
 *  called by:  betos_spiread, sendEEPROMCommand
 *
 *  Parameter:
 *
 *      channel      channel number as set in the channel's
 *                   device table entry
 *  Return Values:
 *
 *      EXIT_SUCCESS
 *	EAGAIN          the semaphore was already locked,
 *      EINVAL          'sem' is not a valid semaphore,
 *      ENOSYS          semaphores are not supported,
 *      EINTR           the function was interrupted by a signal,
 *      EDEADLK         a deadlock condition was detected.
 */
static int spi_init_rx_dma(ONSPI_CHAN  *pch)  /* set up the dma for input and start it */
{
    int channel;
    SPIBUFFER volatile *q;
    DMA_CTRL_REG_TYPE *ptr_dmareg;
    int status;
    
    if (pch->slaveMode)
    {
	status = sem_trywait(pch->spi_int_sem);
	if (status != TX_SUCCESS)
	{/* something is wrong */
	    sem_post(pch->spi_int_sem);
	    return status;
	}
	if (pch->ISRlevel != -1)
	    NAEnableIsr(pch->ISRlevel);
    }
    else
	status = TX_SUCCESS;

    channel = pch->port;
    switch (channel)
    {
        case 0 :
	    ptr_dmareg = DMA_CONF_PTR(DMA7);
	    break;
	case 1:
	     ptr_dmareg = DMA_CONF_PTR(DMA9);
	    break;
    }
     
    q = &(spiInBuffer[channel]);
    
    ser_rx_buffer_des[channel][0].lower_word.bits.wrap = 0;
    ser_rx_buffer_des[channel][0].lower_word.bits.intr = 0;
    ser_rx_buffer_des[channel][0].lower_word.bits.last = 0;
    ser_rx_buffer_des[channel][0].lower_word.bits.sbp =  (UINT32)q->ptrSerData;
    ser_rx_buffer_des[channel][0].upper_word.bits.status = 0;
    ser_rx_buffer_des[channel][0].upper_word.bits.full = 0;
    ser_rx_buffer_des[channel][0].upper_word.bits.buflen =
	(q->countServData & DMA_BUF_LEN_MSK) ; 

    ser_rx_buffer_des[channel][1].lower_word.bits.wrap = 1;
    ser_rx_buffer_des[channel][1].lower_word.bits.intr = 0;
    ser_rx_buffer_des[channel][1].lower_word.bits.last = 0;
    ser_rx_buffer_des[channel][1].lower_word.bits.sbp =  (UINT32)q->ptrDataBuffer;
    ser_rx_buffer_des[channel][1].upper_word.bits.status = 0;
    if (q->countForBufferData == 0)
    {
	ser_rx_buffer_des[channel][1].upper_word.bits.full = 1;
	ser_rx_buffer_des[channel][0].lower_word.bits.wrap = 1;
    }
    else
	ser_rx_buffer_des[channel][1].upper_word.bits.full = 0;

    ser_rx_buffer_des[channel][1].upper_word.bits.buflen =
	(q->countForBufferData & DMA_BUF_LEN_MSK);

    if (pch->SSport != NULL && !pch->slaveMode)
	   *(pch->SSport) &= pch->SSsetLowMask;

     /*now assign buffer descriptor to dma control register*/

    ptr_dmareg->ctrl_reg = 0;
    ptr_dmareg->bd_ptr = (UINT32)ser_rx_buffer_des[channel];
    ptr_dmareg->stat_reg =
	  (DMA_CLAER_ALL_INTERRUPTS |  DMA_NORM_DONE_IE | DMA_ERR_DONE_IE | DMA_CHAN_ABORT_IE);
    ptr_dmareg->ctrl_reg = ( FLY_BY_WRITE | DMA_CHANNEL_ENABLE);
    
#if 0
    intCount = 0;
   printf(" After setup rx_upper_word[0] = %X, rx_lower_word[0] = %X\n",
	   ser_rx_buffer_des[channel][0].upper_word.reg,
	   ser_rx_buffer_des[channel][0].lower_word.reg );
   printf(" After  setup rx_upper_word[1] = %X, rx_lower_word[1] = %X\n",
	   ser_rx_buffer_des[channel][1].upper_word.reg,
	   ser_rx_buffer_des[channel][1].lower_word.reg );
#endif
    
    SER_REG(channel, CTRL_REGA) |= RECV_DMA_ON; /*  And start DMA */
    NAEnableIsr (pch->rxDmalevel);
    return status;
}

/*
 *  Function: netos_spi_enter
 *
 *  Description: First level initialization. Called before
 *               kernel starts.
 *
 *  called by: ANSI layer _enter() (DDIFirstLevelInitalize())
 *
 *  Parameter:
 *
 *      channel      channel number as set in the channel's
 *                   device table entry
 *  Return Values:
 *
 *      EXIT_SUCCESS
 */

int netos_spienter (int i/*channel */)
{

    #if 0
          printf("netos spi init enter \n");
    #endif
    onspiChan[i].rxlevel = devParas[i].rxintLevel;
    onspiChan[i].txlevel = devParas[i].txintLevel;
    onspiChan[i].rxDmalevel = devDmaParas[i].rxintLevel;
    onspiChan[i].txDmalevel = devDmaParas[i].txintLevel;
    onspiChan[i].slaveMode = false;
    onspiChan[i].LSBfirst = false;
    onspiChan[i].hwConfiguration = SIO_SPI_DIRECT;

    onspiChan[i].SSport = NULL;
    onspiChan[i].ISRlevel = -1;
    onspiChan[i].initSeq = true;

    onspiChan[i].blockSEL = false;
    onspiChan[i].slaveTimeOutValue = TX_WAIT_FOREVER; /* For the slave wait forever; */    
	 
    onspiChan[i].startClockHigh = false;
    onspiChan[i].TXCINVset = false;
    onspiChan[i].RXCINVset = false;


    /* Eventually, this will come from non-volatile storage */
    onspiChan[i].baudRate = DEFAULT_SPI_BAUD_RATE;

    /* System port address for this serial channel */
    onspiChan[i].port = i;


    /*
     * initialize UART
     */
    SpiDevInit (&onspiChan[i]);
   
    return EXIT_SUCCESS;
}


/*
 *  Function: netos_spiinit
 *
 *  Description: Second level initialization, after kernel is loaded.
 *               Complete any initialization needed by the driver.
 *
 *  called by: DDISecondLevelInitialize()
 *
 *  Parameter:
 *
 *      channel      channel number as set in the channel's
 *                   device table entry
 *  Return Values:
 *
 *      EXIT_SUCCESS
 *      EACCES	DLC channel open
 */
int netos_spiinit (int i/*channel */)
{

    ONSPI_CHAN * pch;
#if 0
          printf("netos spi init \n");
#endif
    /* create semaphore here */
    sprintf(sem_name[i],"%s%d",semtext,i+1);
    spi_sem[i] = sem_open (sem_name[i], O_EXCL | O_CREAT, 0, 1);
    
    NAInstallIsr (devParas[i].rxvector, (NA_ISR_HANDLER)onspiIntRd, (void*) &onspiChan[i]);  /*install receive interrupt function */
    if (chipRevision < 19)
      NAInstallIsr (devParas[i].txvector, (NA_ISR_HANDLER) onspiIntWr40, (void*) &onspiChan[i]);  /*install send interrupt function */
    else
      NAInstallIsr (devParas[i].txvector, (NA_ISR_HANDLER) onspiIntWr50, (void*) &onspiChan[i]);  /*install send interrupt function */
    NAInstallIsr (devDmaParas[i].txvector, (NA_ISR_HANDLER)onspiDmaWr, (void*) &onspiChan[i]);  /*install send interrupt function */
    NAInstallIsr (devDmaParas[i].rxvector, (NA_ISR_HANDLER)onspiDmaRd, (void*) &onspiChan[i]);  /*install receive interrupt function */

    pch = sysSPIChanals[i];  /* pch points to the device table for channel */

    sprintf(sem_intr_name[i],"%s%d",semintertext,i+1);
    pch->spi_int_sem = sem_open (sem_intr_name[i], O_EXCL | O_CREAT, 0, 1); /* Create semaphore for ISR */

    spiDataBuffer[i].ptrDmaBuffer = NULL;
    spiInBuffer[i].ptrDmaBuffer = NULL;
    
    /* Lock serial channel resource, so HDLC can't open a chsannel */
    if (!GetResource(&serl_resource_lock[i], pch))
	    return (EACCES);	/* HDLC channel open */
	return (EXIT_SUCCESS);
}


/*
 *  Function: netos_spiopen()
 *
 *  Description: open a new session on a channel
 *
 *  called by: open (), creat () in DDI
 *
 *  Parameter:
 *
 *      channel      channel number as set in the channel's
 *                   device table entry
 *      flags        a bit field formed by orring one or more
 *                   of O_RDONLY, O_WRONLY, O_RDWR, O_SPI_SLAVE, or O_SPI_BLOCKSEL
 *
 *  Return Values:
 *
 *      EXIT_SUCCESS
 *      EINVAL
 *      EBUSY
 */
int netos_spiopen (int channel, unsigned flags)
{
  ONSPI_CHAN  *pch;
#define RWOPTIONS   (O_RDONLY|O_WRONLY|O_RDWR|O_DMA)
  unsigned rwmode = (flags & RWOPTIONS);
   
#if 0
   printf("netos spi open \n");
#endif
  
  if (channel != 0 && channel != 1)     /* Only support two serial ports */
        return EINVAL;

  pch = sysSPIChanals[channel];  /* pch points to the device table for channel */

#if 0
 
  rwmode = ((flags & O_RDONLY)/ O_RDONLY) +
           ((flags & O_WRONLY)/ O_WRONLY) +
	   ((flags & O_RDWR)/ O_RDWR);
  if (rwmode != 1)  /* exactly one read /write option must be specified */
      return EINVAL;
#endif

  if (!((rwmode == O_RDONLY) || (rwmode == O_RDWR) || (rwmode == O_WRONLY)))
    {
        return EINVAL;
    }
  
  if ((flags & ~AllSPIOptins) != 0)  /* this is the only illegal value */
      return EINVAL;


  /* return EROFS if flags are inappropriate,
     i.e. Write request on a read only file system */
  
   GET_SEMA(spi_sem[channel])                /* get the semaphore */

   if (pch->opened)/* no need to open if its already open */
   {
      PUT_SEMA(spi_sem[channel])                /* releases semaphore when done */
      return EBUSY;
   }
   
   spiResetChannel (pch,true);     /* clean all interrupts, just in case */
     
   pch->opened = true;                      /* Updated the opened flag */
   pch->openmode = flags;                /* updated the opened mode */

   pch->writeAddr = 0;     /* Starting address for the next write operation */
   pch->readAddr = 0;      /* Starting address for the next read operation */
   pch->IOinProgress = false; /* we are not reading or writing */

   if (pch->initSeq)
   {
       /*
	* Master-Slave or O_SPI_BLOCKSEL
	* mode can be set only during
	* initialization because it will change
	* direction of the electrical signal.
	* It cannnot be done in the middle of the operation
       */

       if ((flags & O_SPI_SLAVE) == O_SPI_SLAVE)
	   pch->slaveMode = true;
       else
	   pch->slaveMode = false;
       
       if ((flags & O_SPI_BLOCKSEL) == O_SPI_BLOCKSEL)
       {
	   if (pch->slaveMode)
	   {
	       /* O_SPI_BLOCKSEL is valid only with the master mode */
	       PUT_SEMA(spi_sem[channel]);
	       return EINVAL;
	   }
	   else
	       pch->blockSEL = true;
       }
       else
	   pch->blockSEL = false;

       pch->initSeq = false;

       onspiInitChannel(pch, false);

       if (pch->SSport != NULL)
	   *(pch->SSport) |= pch->SSsetHiValue;
   }
   
   PUT_SEMA(spi_sem[channel])                /* releases semaphore when done */
   return EXIT_SUCCESS;
}

/*
 *  Function: netos_spiclose()
 *
 *  Description: close session on a channel
 *
 *  called by: close () in DDI
 *
 *  Parameter:
 *
 *      channel      channel number as set in the channel's
 *                   device table entry
 *  Return Values:
 *
 *      EXIT_SUCCESS
 *      EINVAL
 *      EBUSY
 */
int netos_spiclose (int channel)
{
  ONSPI_CHAN  *pch;
  if (channel != 0 && channel != 1)     /* check to see if channel number is valid */
  {
    return EINVAL;
  }

  pch = sysSPIChanals[channel];  /* pch points to the device table for channel */

  pch->IOinProgress = false;


  GET_SEMA(spi_sem[channel])                /* get the semaphore */

  if (!pch->opened)                     /* check to see if device is already closed */
  {
      PUT_SEMA(spi_sem[channel])    /* releases semaphore when done */ 
      return EBADF;
  }

  pch->opened = false;                      /* updates the opened flag */
  pch->openmode = 0;                    /* updates the openmode flag */
  PUT_SEMA(spi_sem[channel])    /* releases semaphore when done */ 
  return EXIT_SUCCESS;
}

/*
 * Function: netos_spiread()
 *
 * Description: read data from driver
 *
 * Called by: DDI read
 *
 * Input : channel - 0 for port com1, 1 for port com2
 *         buffer - data to be stored
 *         length - size of data buffer
 *         bytesWritten - store actual size being sent
 *
 * Return: EBADF 
 *         EINVAL
 *         EBUSY
 *         EAGAIN
 *         EXIT_SUCCESS
 *
 */
int netos_spiread (int channel, void * buffer, int length, int *bytesRead)
{
    ONSPI_CHAN  *pch;
    int err_code = EXIT_SUCCESS;
     
    int status;
    char * ioBuffer;

    *bytesRead = 0;

    if (channel != 0 && channel != 1)     /* Only two channels are valid  */
    {
	return EINVAL;
    }

    pch =  sysSPIChanals[channel];  /* pch points to the serial device for the channel*/

    if (!pch->opened)                     /* make sure device is opened */
    {
	return EBADF;
    }

    if (pch->openmode & O_WRONLY)         /* make sure device is not write only */
    {
	return EINVAL;
    }

    if (length < 0 || length > 32767)     /* check if length is OK */
    {
	return EINVAL;
    }


    GET_SEMA(spi_sem[channel])                /* get the semaphore */

    if (spiDataBuffer[channel].ptrDmaBuffer != NULL)
    {
	free (spiDataBuffer[channel].ptrDmaBuffer);
	spiDataBuffer[channel].ptrDmaBuffer = NULL;
    }
    if (spiInBuffer[channel].ptrDmaBuffer != NULL)
    {
	free (spiInBuffer[channel].ptrDmaBuffer);
	spiInBuffer[channel].ptrDmaBuffer = NULL;
    }

    spiResetChannel(pch,true);			/* Reset serial port */

    pch->readInProgres = true;
    pch->IOinProgress = true; 
    ioBuffer = spi_set_io_buffer (&spiInBuffer[channel],buffer,length,pch);
    if (ioBuffer == NULL)
    {
	err_code = EBADF;
	goto end;
    }
    if (!pch->slaveMode)
    {
	ioBuffer = spi_set_io_buffer (&spiDataBuffer[channel],buffer,length,pch);
	if (ioBuffer == NULL)
	{
	    err_code = EBADF;
	    goto end;
	}
    }
     
    if (pch->openmode & O_DMA)
    {
	status = spi_init_rx_dma(pch);
	if (!pch->slaveMode)
	   status = spi_init_tx_dma(pch);
    }
    else
    {
	status = onspiRxStartup (pch);
	if (!pch->slaveMode)
	    status = onspiTxStartup (pch);
    }
    if (status != TX_SUCCESS)
    {
	err_code = status;
	goto end;
    }
    
    if (!waitForCompletion(&spiInBuffer[channel],pch,length))
    {
	err_code = EAGAIN;
	 printf("Error waitForCompletion for read. Bytes left %d. Len = %d  \n",
	       (spiInBuffer[channel].countServData + spiInBuffer[channel].countForBufferData),length);
	goto end;
    }
    
    *bytesRead = length;
     
    pch->readAddr += length;
    
    spi_copy_io_buffer (&spiInBuffer[channel],buffer,length,pch);

    end:
	
    pch->IOinProgress = false; 
    if (spiDataBuffer[channel].ptrDmaBuffer != NULL)
    {
	free (spiDataBuffer[channel].ptrDmaBuffer);
	spiDataBuffer[channel].ptrDmaBuffer = NULL;
    }
    if (spiInBuffer[channel].ptrDmaBuffer != NULL)
    {
	free (spiInBuffer[channel].ptrDmaBuffer);
	spiInBuffer[channel].ptrDmaBuffer = NULL;
    }
    PUT_SEMA(spi_sem[channel])

    return err_code;

}
/*
 * 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
 *
 */
static int sendWriteEnable(ONSPI_CHAN *pChan )
{
    int channel;
    int status;

    if (pChan->hwConfiguration == SIO_SPI_DIRECT)
	return EXIT_SUCCESS; 

    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;
    }
    return EXIT_SUCCESS;
}
    
/*
 * Function: sendEEPROMCommand()
 *
 * Description: write or read value to/from EEPROM registry
 *
 * Called by: onspiIoctl
 *
 * Input : pChan - Poiner to the cahnnel description
 *         request request from ioctl
 *         arg     argument from ioctl
 *
 * Return: EBADF 
 *         EINVAL
 *         EBUSY
 *         EAGAIN
 *         EXIT_SUCCESS
 *
 */
static int sendEEPROMCommand(ONSPI_CHAN *pChan,int request,int *arg)
{
    int channel;
     
    SPIBUFFER volatile *activBaffer;
    int status;
 
    channel = pChan->port;
   
    if (request == SIO_SET_REG)
    {
	pChan->readInProgres = false;

	/* First enable write operation */

	status = sendWriteEnable(pChan);

	if (status != EXIT_SUCCESS)
	    return status;

	spiResetChannel(pChan,true);  /* Reset the channel because send write is doing IO */
	
	spi_set_command(&spiDataBuffer[channel],XICOR_WRSR ,*arg);
	if (pChan->openmode & O_DMA)
	    status = spi_init_tx_dma(pChan);
	else
	    status = onspiTxStartup (pChan);
	activBaffer = &spiDataBuffer[channel];
    }
    else
    {
	spiResetChannel(pChan,true);  /* Reset the channel because we do not know status */
	spi_set_command(&spiDataBuffer[channel],XICOR_RDSR , *arg);
	spi_set_command (&spiInBuffer[channel],XICOR_RDSR,  *arg);
	pChan->readInProgres = TRUE;
	if (pChan->openmode & O_DMA)
	{
	    spi_init_rx_dma(pChan);
	    status = spi_init_tx_dma(pChan);
	}
	else
	{
	    onspiRxStartup (pChan);
	    status = onspiTxStartup (pChan);
	}
	activBaffer = &spiInBuffer[channel];
    }
    if (status != TX_SUCCESS)
    {
	return (status);
    }
    
    if (!waitForCompletion(activBaffer,pChan,0))
    {
	return EAGAIN;
    }
    
    spi_copy_reply(activBaffer,arg);
     
    return EXIT_SUCCESS; 
}
    
/*
 * Function: netos_spiwrite()
 *
 * Description: write data to driver
 *
 * Called by: DDI write
 *
 * Input : channel - 0 for port com1, 1 for port com2
 *         buffer - data to be sent
 *         length - size of data
 *         bytesWritten - store actual size being sent
 *
 * Return: EBADF  
 *         EINVAL
 *         EBUSY
 *         EAGAIN
 *         EXIT_SUCCESS
 *
 */
int netos_spiwrite (int channel, void * buffer, int length, int *bytesWritten)
{
  int status;
  ONSPI_CHAN  *pch;
  int err_code = EXIT_SUCCESS;

  *bytesWritten = 0;
  
  if (channel != 0 && channel != 1)     /* check if channel is ready */
  {
    return EINVAL;
  }

  if (length < 0 || length > 32767)     /* check if length is OK */
  {
    return EINVAL;
  }


  pch = sysSPIChanals[channel];  /* pch points to device drive table */

  if (!pch->opened)                     /* be sure port is opened */
  {
      return EBADF;
  }

   if (pch->slaveMode && pch->ISRlevel == -1 && length < 40 )
  {
    return EINVAL;
  }

  if (pch->openmode & O_RDONLY)         /* make sure device is not write only */
  {
      return EINVAL;
  }
  
  GET_SEMA(spi_sem[channel])                /* get the semaphore */

      /*
	The following code is probably over kill for EEPROMS
	where we are writing tens of bytes. May be useful for
	the application that writes hundred of bytes.
	*/
  
   if (spiDataBuffer[channel].ptrDmaBuffer != NULL)
    {
	free (spiDataBuffer[channel].ptrDmaBuffer);
	spiDataBuffer[channel].ptrDmaBuffer = NULL;
    }
  
  pch->readInProgres = false;
  pch->IOinProgress = true; 

  /* First enable write operation */

  err_code = sendWriteEnable(pch);

  if (err_code != EXIT_SUCCESS)
      goto end;

  /* now write data itself */

  spiResetChannel(pch,true);  /* Reset the channel because send write is doing IO */
  
  spi_set_io_buffer (&spiDataBuffer[channel], buffer, length, pch);
   
  if (pch->openmode & O_DMA)
      status = spi_init_tx_dma(pch);
  else
      status = onspiTxStartup (pch);

  if (status != TX_SUCCESS)
  {
      err_code = status;
      goto end;
  }

  if (!waitForCompletion(&spiDataBuffer[channel],pch,length))
  {
      err_code = EAGAIN;
       printf("Error in waitForCompletion for write . Bytes left %d. Len = %d  \n",
	       (spiDataBuffer[channel].countServData + spiDataBuffer[channel].countForBufferData),length);
      goto end;
  }
   
   
  *bytesWritten = length ;
   
  pch->writeAddr += length;
  
end:
    pch->IOinProgress = false;
    if (spiDataBuffer[channel].ptrDmaBuffer != NULL)
    {
	free (spiDataBuffer[channel].ptrDmaBuffer);
	spiDataBuffer[channel].ptrDmaBuffer = NULL;
    }
    
  PUT_SEMA(spi_sem[channel])

  return err_code;

}

/*
 * Function: netos_spiioctl()
 *
 * Description This function calls onspiIoctl to sets the serial parameters
 * returns
 *              EINVAL when channel is not valid
 *              EBADF when channel is not opened
 *              err_code returned from onspiIoctl
 */
int netos_spiioctl (int channel, int request, char *args)
{
  int err_code = EXIT_SUCCESS;
  ONSPI_CHAN  *pch;

  if (channel != 0 && channel != 1)     /* two valid channels */
        return EINVAL;

  pch = sysSPIChanals[channel];

  GET_SEMA(spi_sem[channel])      /* get the semaphore */

  pch->IOinProgress = false; 
  if (!pch->opened)                /* Port has to be opened */
  {
      PUT_SEMA(spi_sem[channel])   /* releases semaphore when done */
       return EBADF;
  }
      
#if 0
  printf("netos spi ioctl \n");
#endif
  
  err_code = onspiIoctl ( pch, request, (int *)args);
  
  PUT_SEMA(spi_sem[channel]) 
  return err_code;
}


/**********************************************************************************

 **********************************************************************************
 ***                                                                            ***
 *** Function Name:  onspiInitChannel()                                         ***
 ***                                                                            ***
 *** Called by:      SpiDevInit()                                               ***
 ***                                                                            ***
 *** Comments:  Hardware initialization of the NCC serial channel.              ***
 **********************************************************************************
 **********************************************************************************/

static void onspiInitChannel (ONSPI_CHAN * pChan, bool initSeq)
{
    if (initSeq)
	blockABCreg(pChan); /* Prevent form sending out signals before all registries are set */
     
    /*** Control Register A Configuration ***/
    /* Enable the serial channel */
    SER_REG (pChan->port, CTRL_REGA) = 0;  /* init ctrl reg, this turns off ints */

    /*** Control Register B Configuration ***/
    /* Match timer features are not used */
    SER_REG (pChan->port, CTRL_REGB) = 0;
    SER_REG (pChan->port, RECV_CHAR_TIMER_REG) = 0;
    SER_REG (pChan->port, RECV_BUF_TIMER_REG) = 0;

    /*** Receive Match Register Configuration ***/
    SER_REG (pChan->port, RECV_MATCH_REG) = 0;

     /*** Receive Buffer Gap Timer Register Configuration ***/
    SER_REG (pChan->port, RECV_BUF_TIMER_REG) = 0;

    /*** Receive Character Timer Register Configuration ***/
    SER_REG (pChan->port, RECV_CHAR_TIMER_REG) = 0;

    SER_REG (pChan->port, BAUD_REG) = 0;


     /*** Control Register B Configuration ***/
    setCtrRebB(pChan);

    setBitRate(pChan); /*** Bit-Rate (baud) Register Configuration ***/
     
    
   
    OptSet(pChan);    /* go set up options in Reg A */

    chipRevision = narm_read_reg(NARM_GSSR_REG, NARM_GSSR_ADDR, rev);

    /*
     * At this point registries for the serial port are configure
     * GPIO registries A, B, and C are configure as none special functions in the 
     * input mode
     * As the result all output signals from the SPI module are blocked.
     * netos_spiopen will configure everything
     * as it should be 
    */
    if (!initSeq)
	 setABCreg(pChan);
    
}
/*
 * 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
 *
 */
static 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 Name:  setCtrRebB                                                 ***
 ***                                                                            ***
 *** Called by:      onspiInitChannel()                                         ***
 ***                                                                            ***
 *** Comments:  Hardware initialization Control Register B                      ***
 **********************************************************************************
 **********************************************************************************/
static void setCtrRebB (ONSPI_CHAN * pChan)

{
    UINT32 reg;

    if (pChan->slaveMode)
	reg = SPI_SLAVE_MODE_VALUE;
    else
	reg = SPI_MASTER_MODE_VALUE;

    if (pChan->LSBfirst)
	reg |= SPI_LSB_FIRST_VALUE;
    else
	reg |= SPI_MSB_FIRST_VALUE;

    SER_REG (pChan->port, CTRL_REGB) = reg;
     
}
 /*********************************************************************************
 **********************************************************************************
 ***                                                                            ***
 *** Function Name:  setABCreg                                                  ***

 ***                                                                            ***
 *** Called by:      onspiInitChannel()                                         ***
 ***                                                                            ***
 *** Comments:  Hardware initialization PORTA, PORTB, and PORTC registry        ***
 **********************************************************************************
 **********************************************************************************/
static void setABCreg (ONSPI_CHAN * pChan)

{
    if (pChan->port == 0)
    {
		if (narm_read_reg(NARM_GSSR_REG, NARM_GSSR_ADDR, rev) >= NETA20UM)
		{
			if (pChan->slaveMode)
			{
				*NARM_PORTA_ADDR = SPI_20UM_PORTA_SLAVE;
			 
			}
			else
			{
				*NARM_PORTA_ADDR = SPI_20UM_PORTA_MASTER;
			}
		}
		else
		{
			/* Prepare mask for port C */
			*NARM_PORTC_ADDR &= PORTC_CHA_MASK;
			/* And port A */
			*NARM_PORTA_ADDR &= PORTAB_MASK;
	
			if (pChan->slaveMode)
			{
				*NARM_PORTA_ADDR |= PORTAB_SLAVE_VALUE;
				*NARM_PORTC_ADDR |= PORTC_CHA_SLAVE_VALUE;
			}
			else
			{
				if (pChan->blockSEL && chipRevision < 0X19) /* Netarm 40 use PORTA4 for SEL */
					*NARM_PORTA_ADDR |=PORTAB_BLICKSEL_VALUE;
				else
					*NARM_PORTA_ADDR |= PORTAB_MASTER_VALUE;

				if (!pChan->blockSEL || chipRevision < 0X19) /* Netarm 50 use PORTC for SEL */
				*NARM_PORTC_ADDR |= PORTC_CHA_MASTER_VALUE;
	    
			}
		}
    }
    else
    {
		if (narm_read_reg(NARM_GSSR_REG, NARM_GSSR_ADDR, rev) >= NETA20UM)
		{
			if (pChan->slaveMode)
			{
				*NARM_PORTC_ADDR = SPI_20UM_PORTC_SLAVE;
			 
			}
			else
			{
				*NARM_PORTC_ADDR = SPI_20UM_PORTC_MASTER;
			}
		}
		else
		{
			/* Prepare mask for port C */  
			*NARM_PORTC_ADDR &= PORTC_CHB_MASK;

			/* And port B */

			*NARM_PORTB_ADDR &= PORTAB_MASK;
	
			if (pChan->slaveMode)
			{
				*NARM_PORTB_ADDR |= PORTAB_SLAVE_VALUE;
				*NARM_PORTC_ADDR |= PORTC_CHB_SLAVE_VALUE;
			}
			else
			{
				if (pChan->blockSEL && chipRevision < 0X19) /* Netarm 40 use PORTB4 for SEL */
					*NARM_PORTB_ADDR |=PORTAB_BLICKSEL_VALUE;
				else
					*NARM_PORTB_ADDR |= PORTAB_MASTER_VALUE;

				if (!pChan->blockSEL || chipRevision < 0X19) /* Netarm 50 use PORTC for SEL */
					*NARM_PORTC_ADDR |= PORTC_CHB_MASTER_VALUE;
			}
		}
    }
  
#if 0
    /* PORTC0 used for timing set it HI */
    *NARM_PORTC_ADDR |= 4;
     reg = *NARM_PORTC_ADDR;
     reg = *NARM_PORTB_ADDR;
     reg = *NARM_PORTA_ADDR;
#endif
   
}

/*********************************************************************************
 **********************************************************************************
 ***                                                                            ***
 *** Function Name:  blockABCreg                                                ***
 ***                                                                            ***
 *** Called by:      onspiInitChannel()                                         ***
 ***                                                                            ***
 *** Comments:  Block output signal for PORTA, PORTB, and PORTC registry        ***
 ***            by setting all port as a normal input. setABCreg will set a     ***
 ***            correct value for this regesters                                ***
 **********************************************************************************
 **********************************************************************************/
static void blockABCreg (ONSPI_CHAN * pChan)

{
    if (pChan->port == 0)
    {
		if (narm_read_reg(NARM_GSSR_REG, NARM_GSSR_ADDR, rev) >= NETA20UM)
		{
			*NARM_PORTA_ADDR = GPIO_PORT_IS_NOT_USED;
		}
		else
		{
			*NARM_PORTA_ADDR &= PORTAB_MASK;
			*NARM_PORTC_ADDR &= PORTC_CHA_MASK;
		}
    }
	 
    else
    {
		if (narm_read_reg(NARM_GSSR_REG, NARM_GSSR_ADDR, rev) >= NETA20UM)
		{
			*NARM_PORTC_ADDR = GPIO_PORT_IS_NOT_USED;
		}
		else
		{
			*NARM_PORTB_ADDR &= PORTAB_MASK;
			*NARM_PORTC_ADDR &= PORTC_CHB_MASK;
		}
    }
}
/*
 * Function: spiResetChannel ()
 *
 * Description: reset serial port registry.
 *              This routine will disable all interrupts.
 *              When called from all routines but waitForCompletion, it will restart serial port.
 *
 * Called by: waitForCompletion, onspiInitChannel,
 *            netos_spiwrite,netos_spiread sendWriteEnable, sendEEPROMCommand
 *
 * Input : pChan - pointer to the channel description
 *         resetPort - true if require to restart serial port
 *
 *
 */
static void spiResetChannel (const ONSPI_CHAN *pChan, const bool resetPort )
{
/* reset serial port registries */
    if (resetPort)
	OptSet(pChan);
    else
	SER_REG (pChan->port, CTRL_REGA) &= REGA_INTFLAGS_MASK;
    
/* disable all serial interrupts */
    NADisableIsr (pChan->rxlevel);
    NADisableIsr (pChan->txlevel);
    NADisableIsr (pChan->txDmalevel);
    NADisableIsr (pChan->rxDmalevel);
    if (pChan->ISRlevel != -1)
	NADisableIsr(pChan->ISRlevel);
    
}


/**********************************************************************************
 **********************************************************************************
 ***                                                                            ***
 *** Function Name:  OptSet()                                                   ***
 ***                                                                            ***
 *** Called by:      onccInitChannel()                                          ***
 ***                                                                            ***
 *** Comments: This routine sets up the hardware (serial Control Register A)    ***
 **********************************************************************************
 **********************************************************************************/
static void OptSet( const ONSPI_CHAN * pChan)
{
 

    UINT32 reg;
     
    SER_REG (pChan->port, CTRL_REGA) = 0;               /* reset port */
    reg = SER_CHANNEL_ENABLE | CTRL_EIGHT_BITS;         /* channel is always enabled with 8 bits*/
    SER_REG (pChan->port, CTRL_REGA) = reg;

}

/**********************************************************************************
 **********************************************************************************
 ***                                                                            ***
 *** 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.
 **********************************************************************************
 **********************************************************************************/

static 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;
}

/**********************************************************************************
 **********************************************************************************
 ***                                                                            ***
 *** 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.                                                                   ***
 ***                                                                            ***
 **********************************************************************************
 **********************************************************************************/

static 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;
	    
	case SIO_GET_REG:
	case SIO_SET_REG:
	    status = sendEEPROMCommand(pChan,request,arg);
	    break;
	    
	case SIO_SLAVE_GET:
	    status = OK;
	    *arg = pChan->slaveMode;
	    break;
	    
	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;

#ifdef BSP_ARM9
    
	case SIO_SET_LSB :
	    status = OK;
	    pChan->LSBfirst = *arg;
	    reg = SER_REG (pChan->port, CTRL_REGB);
	    if (pChan->LSBfirst)
		reg &= ~SPI_MSB_FIRST_VALUE;
	    else
		reg |= SPI_MSB_FIRST_VALUE;
	    SER_REG (pChan->port, CTRL_REGB) = reg;
	    break;

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

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

	case SIO_SPI_HW_SET:
	    switch (*arg)
	    {
		case SIO_SPI_DIRECT:
		case SIO_SPI_X25020:
		case SIO_SPI_X25C640U:
		    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;

    }

    return (status);
}

/**********************************************************************************
 **********************************************************************************
 ***                                                                            ***
 *** Function Name:  SpiDevInit()                                               ***
 ***                                                                            ***
 *** Called by:                                                                 ***
 ***                                                                            ***
 *** Comments: This routine initializeS some SIO_CHANfunction pointers and     ***
 *** then resets the serial port in a quiescent state.  Before this routine is  ***
 *** called, the BSP must already have initialized all the device addresses,    ***
 *** etc. in the ONSPI_CHAN structure.  (Reference WRS template & example.)      ***
 **********************************************************************************
 **********************************************************************************/

static void SpiDevInit (ONSPI_CHAN * pChan)
{ 

   onspiInitChannel(pChan,true);

}
 
 
/**********************************************************************************
 **********************************************************************************
 ***                                                                            ***
 *** Function Name:  onspiIntWr40()                                             ***
 ***                                                                            ***
 *** Called by:                                                                 ***
 *** Function Calls: None                                                       ***
 ***                                                                            ***
 *** Comments: This routine handles write interrupts from the NCC.              ***
 **********************************************************************************
 **********************************************************************************/

static void onspiIntWr40 (ONSPI_CHAN * pChan)
{
    char outChar;
    UINT32 reg;
    SPIBUFFER volatile *q;
       
#if 0
    /* PORTC0 used for timing set it HI */
    reg = SER_REG (pChan->port, CTRL_REGA);
    if (intCount < 40)
	statreg[intCount++] = (SER_REG (pChan->port, STAT_REG)); 
    *NARM_PORTC_ADDR |= 4;
#endif

    q = &(spiDataBuffer[pChan->port]);
    
    if ( (q->countServData + q->countForBufferData)  == 0)
    { /* We send out all data */ 

        /* disable transmitter interrupt */
        reg = SER_REG (pChan->port, CTRL_REGA);
        reg &= (~XMITR_CLOSED_IRQ);
	reg &= (~XMITR_HALF_FULL_IRQ);
        SER_REG (pChan->port, CTRL_REGA) = reg;
	/* if no read or slave - release samaphore */
	 if ( (!pChan->readInProgres || pChan->slaveMode ) && (pChan->ISRlevel == -1))
	    tx_semaphore_put (&((pChan->spi_int_sem)->sem));
	 return;
    }
    
    if (q->countServData != 0)
    {
	/* remove a char from the server buffer */
	outChar = *(q->ptrSerData++);
	q->countServData--;
	SER_REG_BYTE (pChan->port, DATA_REG) = outChar;
    }
    else
    {
	if (q->countForBufferData !=0 )
	{
	/* remove byte from the main buffer */
	    outChar = *(q->ptrDataBuffer++);
	    q->countForBufferData--;
	    SER_REG_BYTE (pChan->port, DATA_REG) = outChar;
	}
    }
    if ((q->countServData + q->countForBufferData)  == 0)
    { /* We send out all data */
         
        reg = SER_REG (pChan->port, CTRL_REGA);
        reg &= (~XMITR_RDY_IRQ); 
        reg |= XMITR_HALF_FULL_IRQ; /* Problem with HW. We need only XMITR_CLOSED_IRQ at this time */
	reg |= XMITR_CLOSED_IRQ ;
        SER_REG (pChan->port, CTRL_REGA) = reg;
    }
#if 0
    /* PORTC2 used for timing set it LOW */
    if (intCount < 40)
	statreg[intCount++] = (SER_REG (pChan->port, STAT_REG));
    *NARM_PORTC_ADDR &= 0xFEFFFFFB; 
#endif
}

/**********************************************************************************
 **********************************************************************************
 ***                                                                            ***
 *** Function Name:  onspiIntWr50()                                             ***
 ***                                                                            ***
 *** Called by:                                                                 ***
 *** Function Calls: None                                                       ***
 ***                                                                            ***
 *** Comments: This routine handles write interrupts from the NCC.              ***
 **********************************************************************************
 **********************************************************************************/
static void onspiIntWr50 (ONSPI_CHAN * pChan)
{
    char outChar;
    UINT32 reg;
    SPIBUFFER volatile *q;
       
#if 0
    /* PORTC0 used for timing set it HI */
    *NARM_PORTC_ADDR |= 4;
    reg = SER_REG (pChan->port, CTRL_REGA);
    if (intCount < 40)
	statreg[intCount++] = (SER_REG (pChan->port, STAT_REG)); 
   
#endif
 
    q = &(spiDataBuffer[pChan->port]);
    
    if ( (q->countServData + q->countForBufferData)  == 0)
    { /* We send out all data  */

        /* disable transmitter interrupt */
        reg = SER_REG (pChan->port, CTRL_REGA);
	reg &= (~XMITR_RDY_IRQ); 
        reg &= (~XMITR_CLOSED_IRQ);
	reg &= (~XMITR_HALF_FULL_IRQ);
        SER_REG (pChan->port, CTRL_REGA) = reg;
	/* if no read or slave - release samaphore */
	 if ( (!pChan->readInProgres || pChan->slaveMode ) && (pChan->ISRlevel == -1) )
	    tx_semaphore_put (&((pChan->spi_int_sem)->sem));
#if 0
        /* PORTC2 used for timing set it LOW */
	 *NARM_PORTC_ADDR &= 0xFEFFFFFB; 
	 if (intCount < 40)
	     statreg[intCount++] = (SER_REG (pChan->port, STAT_REG)); 
#endif
	 return;
    }
 
    if (q->countServData != 0)
    {
	/* remove a char from the server buffer */
	outChar = *(q->ptrSerData++);
	q->countServData--;
	SER_REG_BYTE (pChan->port, DATA_REG) = outChar;
    }
    else
    {
	if (q->countForBufferData !=0 )
	{
	/* remove byte from the main buffer */
	    outChar = *(q->ptrDataBuffer++);
	    q->countForBufferData--;
	    SER_REG_BYTE (pChan->port, DATA_REG) = outChar;
	}
    }
#if 0
    /* PORTC2 used for timing set it LOW */
    *NARM_PORTC_ADDR &= 0xFEFFFFFB; 
    if (intCount < 40)
	statreg[intCount++] = (SER_REG (pChan->port, STAT_REG)); 
#endif
}

/**********************************************************************************
 **********************************************************************************
 ***                                                                            ***
 *** Function Name:  onspiIntRd()                                               ***
 ***                                                                            ***
 *** Called by:                                                                 ***
 *** Function Calls: None                                                       ***
 ***                                                                            ***
 *** Comments: This routine handles read interrupts from the NCC.        	***
 **********************************************************************************
 **********************************************************************************/
void onspiIntRd (ONSPI_CHAN * pChan)
{
    UINT32 reg ;
    UINT32 inchar;
    UINT8  *ptr;
    int nread;
    int i;
    SPIBUFFER volatile *q;

#ifdef debug_spi
    /* PORTC0 used for timing set it HI */
    reg = SER_REG (pChan->port, CTRL_REGA);
    if (intCount < 40)
	statreg[intCount++] = (SER_REG (pChan->port, STAT_REG)); 
    *NARM_PORTC_ADDR |= 4;
#endif


    reg = (SER_REG(pChan->port, STAT_REG));
    
    SER_REG(pChan->port, STAT_REG) |= CLR_ALL_RX_CONDITIONS;
    inchar = SER_REG(pChan->port, DATA_REG);

#if 0
     /* PORTC0 used for timing set it HI */
     
    if (intCount < 40)
	statreg[intCount++] = reg;
    if (intCount < 40)
	statreg[intCount++] = SER_REG(pChan->port, STAT_REG); 
#endif
    
    q = &(spiInBuffer[pChan->port]);

    nread = 0;
    if (reg & RECV_RDY)
    {
	nread = (reg  & NUM_DATA_BYTES) >> 20;
	if (nread == 0)
	    nread = 4;
    }
    ptr = (UINT8*) &inchar;
    
    for (i = 0 ; i < nread; i++)
    {
	if (q->countServData != 0)
	{
	    /* add a char from the server buffer */
	    *(q->ptrSerData++) = *(ptr+i);
	    q->countServData--;
	}
	else
	{
	    if (q->countForBufferData !=0 )
	    {
		/* add byte from the main buffer */
		*(q->ptrDataBuffer++) = *(ptr+i);
		q->countForBufferData--;
	    }
	}
    }                     /* End of for nread*/
    
    if ((q->countServData + q->countForBufferData) == 0)
    {

	/* disable transmitter interrupt */
        reg = SER_REG (pChan->port, CTRL_REGA);
        reg &= ~(RECV_RDY_IRQ |RECV_CLOSED_IRQ|RECV_HALF_FULL_IRQ);
        SER_REG (pChan->port, CTRL_REGA) = reg;
	if (pChan->ISRlevel == -1)
	    tx_semaphore_put (&((pChan->spi_int_sem)->sem));
    }
     SER_REG(pChan->port, STAT_REG) |= RECV_CLOSED;
#ifdef debug_spi
     /* PORTC0 used for timing set it HI */
     
    if (intCount < 40)
	statreg[intCount++] = SER_REG(pChan->port, STAT_REG); 
    /* PORTC2 used for timing set it LOW */
    *NARM_PORTC_ADDR &= 0xFEFFFFFB; 
#endif
}
/**********************************************************************************
 **********************************************************************************
 ***                                                                            ***
 *** Function Name:  onspiISRforSel()                                           ***
 ***                                                                            ***
 *** Called by:                                                                 ***
 *** Function Calls: None                                                       ***
 ***                                                                            ***
 *** Comments: This routine handles  interrupts from SEL* signal .              ***
 **********************************************************************************
 **********************************************************************************/
static void onspiISRforSel(ONSPI_CHAN * pChan)
{
    WORD32 reg;

    /*
     * We need to write 1 to the pChan->ISRbit, do not write 1 to the bits
     * Designated for the special functions and do not change the rest of bits
     */

    
    reg =  (*NARM_PORTC_ADDR) >> 24; /* Move special function to the right bits */
    reg = ~reg | pChan->ISRbit;    /* Mask out all special function bits and set ISRbit*/
    *NARM_PORTC_ADDR &= reg;

    if (pChan->IOinProgress)
	/* Release semaphore so WaitForCompletion could continue */
	tx_semaphore_put (&((pChan->spi_int_sem)->sem));
     
}
/**********************************************************************************
 **********************************************************************************
 ***                                                                            ***
 *** Function Name:  onspiDmaRd()                                                ***
 ***                                                                            ***
 *** Called by:                                                                 ***
 *** Function Calls: None                                                       ***
 ***                                                                            ***
 *** Comments: This routine handles Dma read interrupts from the NCC.           ***
 **********************************************************************************
 **********************************************************************************/

void onspiDmaRd (ONSPI_CHAN * pChan)                       /* interrupt function for DMA channel 9 which is for com1. */
{

   SPIBUFFER volatile *q;
   DMA_CTRL_REG_TYPE *ptr_dmareg;
   int channel;

   
    channel = pChan->port;
    
    switch (channel)
    {
        case 0 :
	    ptr_dmareg = DMA_CONF_PTR(DMA7);
	    break;
	case 1:
	     ptr_dmareg = DMA_CONF_PTR(DMA9);
	    break;
    }
    
#if 0
    /* PORTC2 used for timing set it HI */
    *NARM_PORTC_ADDR |= 4;
    reg9st[intCount] = ptr_dmareg->stat_reg;
    reg9cr[intCount++] = ptr_dmareg->ctrl_reg;
    rx_upper_word[0] = ser_rx_buffer_des[channel][0].upper_word.reg;
    rx_lower_word[0] = ser_rx_buffer_des[channel][0].lower_word.reg;
    rx_upper_word[1] = ser_rx_buffer_des[channel][1].upper_word.reg;
    rx_lower_word[1] = ser_rx_buffer_des[channel][1].lower_word.reg;
#endif

    
    
   if ( (ptr_dmareg->stat_reg & DMA_NORM_DONE_IP) == DMA_NORM_DONE_IP)  
    {
	ptr_dmareg->ctrl_reg = 0;

	q = &(spiInBuffer[channel]);

	if (ser_rx_buffer_des[channel][0].upper_word.bits.full == 1)
	    q->countServData -=
	      ser_rx_buffer_des[channel][0].upper_word.bits.buflen ;

	if (ser_rx_buffer_des[channel][1].upper_word.bits.full == 1)
	    q->countForBufferData -=
		ser_rx_buffer_des[channel][1].upper_word.bits.buflen;

	if (pChan->ISRlevel == -1)
	    tx_semaphore_put (&((pChan->spi_int_sem)->sem));
    }
   ptr_dmareg->stat_reg |= DMA_CLAER_ALL_INTERRUPTS;
   
#if 0
    /* PORTC2 used for timing set it LOW */
    *NARM_PORTC_ADDR &= 0xFEFFFFFB;
#endif
   
}


/**********************************************************************************
 **********************************************************************************
 ***                                                                            ***
 *** Function Name:  onspiDmaWr()                                                ***
 ***                                                                            ***
 *** Called by:                                                                 ***
 *** Function Calls: None                                                       ***
 ***                                                                            ***
 *** Comments: This routine handles Dma write interrupts from the NCC.          ***
 **********************************************************************************

 **********************************************************************************/

static void onspiDmaWr (ONSPI_CHAN * pChan)
{
    
    SPIBUFFER volatile *q;
    DMA_CTRL_REG_TYPE *ptr_dmareg;
    int channel;

    channel = pChan->port;
    
    switch (channel)
    {
        case 0 :
	    ptr_dmareg = DMA_CONF_PTR(DMA8);
	    break;
	case 1:
	     ptr_dmareg = DMA_CONF_PTR(DMA10);
	    break;
    }
#ifdef debug_spi

    /*  PORTC0 used for timing set it HI */
    *NARM_PORTC_ADDR |= 4;
    
    reg10st = *NARM_DSIER10_ADDR;/* (*NCC_DMA).dmasr10.reg; */
    reg10cr = *NARM_DCR10_ADDR; /* (*NCC_DMA).dmacr10.reg;*/

    tx_upper_word[intCount] = ser_tx_buffer_des[channel][0].upper_word.reg;
    tx_lower_word[intCount] = ser_tx_buffer_des[channel][0].lower_word.reg;
    tx_upper_word[intCount+1] = ser_tx_buffer_des[channel][1].upper_word.reg;
    tx_lower_word[intCount+1] = ser_tx_buffer_des[channel][1].lower_word.reg;

    intCount += 2;
#endif
    
    if ( (ptr_dmareg->stat_reg & DMA_NORM_DONE_IP) == DMA_NORM_DONE_IP)
    {
	ptr_dmareg->ctrl_reg = 0;


	q = &(spiDataBuffer[channel]);

	if (ser_tx_buffer_des[channel][0].upper_word.bits.full == 0)
	    q->countServData -=
		ser_tx_buffer_des[channel][0].upper_word.bits.buflen ;

	if (ser_tx_buffer_des[channel][1].upper_word.bits.full == 0)
	    q->countForBufferData -=
		ser_tx_buffer_des[channel][1].upper_word.bits.buflen;

	/* if no read or slave - release samaphore */
	 if ((!pChan->readInProgres || pChan->slaveMode ) && (pChan->ISRlevel == -1))
	    tx_semaphore_put (&((pChan->spi_int_sem)->sem));
    }
   ptr_dmareg->stat_reg |= DMA_CLAER_ALL_INTERRUPTS;
   
#if 0
    /* PORTC2 used for timing set it LOW */
    *NARM_PORTC_ADDR &= 0xFEFFFFFB;
#endif
   
}


 
/**********************************************************************************
 **********************************************************************************
 ***                                                                            ***
 *** Function Name:  onspiRxStartup()                                           ***
 ***                                                                            ***
 *** Called by:                                                                 ***
 ***                                                                            ***
 *** Comments: Call interrupt level character output routine and enable         ***
 *** interrupt.  Function returns: OK on success, ENOSYS if the device is       ***
 *** polled-only, or EIO on hardware error.                                     ***
 **********************************************************************************
 **********************************************************************************/
static int onspiRxStartup (ONSPI_CHAN* pChan)
{
    int status;

    if (pChan->slaveMode)
    {
	status = sem_trywait(pChan->spi_int_sem);
	if (status != TX_SUCCESS)
	{/* something is wrong */
	    sem_post(pChan->spi_int_sem);
	    return status;
	}
	if (pChan->ISRlevel != -1)
	    NAEnableIsr(pChan->ISRlevel);
    }
    else
	status = TX_SUCCESS;

    if (pChan->SSport != NULL && !pChan->slaveMode)
	*(pChan->SSport) &= pChan->SSsetLowMask;
    
    /* tx int is enabled in tx_startup() */
    NAEnableIsr (pChan->rxlevel);
    
    SER_REG (pChan->port, CTRL_REGA) |= (RECV_RDY_IRQ |RECV_CLOSED_IRQ|RECV_HALF_FULL_IRQ);

    return status;
    
}
/**********************************************************************************
 **********************************************************************************
 ***                                                                            ***
 *** Function Name:  onspiTxStartup()                                           ***
 ***                                                                            ***
 *** Called by:                                                                 ***
 ***                                                                            ***
 *** Comments: Call interrupt level character output routine and enable         ***
 *** interrupt.  Function returns: OK on success, ENOSYS if the device is       ***
 *** polled-only, or EIO on hardware error.                                     ***
 **********************************************************************************

 **********************************************************************************/

static int onspiTxStartup (ONSPI_CHAN* pChan)
{

    int status;
    
    status = sem_trywait(pChan->spi_int_sem);
    if (status != TX_SUCCESS)
    {/* something wrong */
	sem_post(pChan->spi_int_sem);
	return status;
    }
    if (pChan->ISRlevel != -1)
	NAEnableIsr(pChan->ISRlevel);
    
    if (pChan->SSport != NULL && !pChan->slaveMode)
	*(pChan->SSport) &= pChan->SSsetLowMask;
    
    /* enable transmitter interrupts */
    NAEnableIsr (pChan->txlevel);
    /* We need
     * XMITR_HALF_FULL_IRQ to add data to the FIFO
     * XMITR_CLOSED_IRQ to clean it
     * XMITR_RDY_IRQ to know that XMITR_RDY_IRQ clear has been accknowledged by the HW
       and we can write data to the FIFO.
     */
    SER_REG (pChan->port, CTRL_REGA) |= XMITR_RDY_IRQ  ;
     

    return status;
}
/*
 * 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
 *
 *
 */
 
static 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
 *
 */

static 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: InstallSpiDevice ()
 *
 * This function goes through the deviceTable to find an empty slot to install
 * the serial device. The first empty slot will be used.
 */
int InstallSpiDevice(char *port)
{
    int i=0;

    while (i < MAX_DEV && deviceTable[i].name)  /* check for empty device slot */
        i++;
    if (i == MAX_DEV)
        return -1;

    INSTALL_DEVICE (i,                  /* registers the serial device and its driver functions */
        port,
        0,
        netos_spienter,
        netos_spiinit,
        netos_spiopen,
        netos_spiclose,
        NULL,
        netos_spiread,
        netos_spiwrite,
        netos_spiioctl,
        (unsigned)-1);

    return 0;
}

/*
 * 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
 *
 */
 
static 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)
		fifoleft = 20;
	    else
	    {
		if (chipRevision < 19)
		    fifoleft = 3;
		else
		    fifoleft = 6;
	    }

	    leftOver = (numberOfBytes < fifoleft) ? numberOfBytes : fifoleft; /* calcNumOfTicks will add 2 bytes */
	    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)
   {
       printf("Error from tx_semaphore. Bytes left %d. Len = %d  \n",
	       (q->countServData + q->countForBufferData),numberOfBytes);
       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
 *
 */
static bool isBufferEmpty (const SPIBUFFER volatile *q)
{
    bool ret;
    TX_INTERRUPT_SAVE_AREA
    TX_DISABLE    

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

    TX_RESTORE
    return ret;
}

/*
 * Function: spi_set_command
 *
 * Description: prepare SPI buffer form sending command to EEPROM
 *
 * Called by: sendEEPROMCommand, 
 *
 * Input : q- pointer to the data buffer
 *         request - requested command
 *         arg     - arguments for the requested command 
 *          
 *         
 */
static void spi_set_command(SPIBUFFER volatile *q,char request,int arg)
{
    

    q->ptrSerData = (char *)q->serBufferData.bufferData;
    q->serBufferData.bufferData[0] = request ;
    q->serBufferData.bufferData[1] = arg ;
    q->countServData = 2;           
    q->ptrDataBuffer = NULL;
    q->countForBufferData = 0;
}

/*
 * Function: spi_copy_reply
 *
 * Description: copy reply received from  EEPROM
 *
 * Called by: sendEEPROMCommand, 
 *
 * Input : q- pointer to the data buffer
 *         arg     - pointer to the int to hold reply 
 *          
 *         
 */
static void spi_copy_reply(SPIBUFFER volatile *q,int *arg)
{
    
    *arg = q->serBufferData.bufferData[1];
}
/*
 * 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; 
}
/*
 * 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
 *          
 *         
 */
static void spi_copy_io_buffer (SPIBUFFER volatile *q, char *c, int nbytes,ONSPI_CHAN *pChan)
{
    int offset;

    
    if (pChan->hwConfiguration == SIO_SPI_DIRECT)
		offset = 0;
    else if (pChan->hwConfiguration == SIO_SPI_X25C640U)
		offset = 3;
	else
		offset = 2;

    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);
    }
}
/*
 * 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
 * 
 */
static char * spi_set_io_buffer (SPIBUFFER volatile *q, char *c, int nbytes,ONSPI_CHAN *pChan)
{
    int tempBytes;
    int offset;

    q->ptrDmaBuffer = NULL;

    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;
	

    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
	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;
}
 
/*
 * 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).
*/
static 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
