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

#include "Npttypes.h"
#include "reg_def.h"
#include "nammu.h"
#include "nainvalidate.h"
#include "NA_ISR.h"
#include "netoserr.h"
#include "nawait.h"

#include "spi_slave.h"

#define LOCAL_SPI_PORT_SLAVE    BSP_SPI_SLAVE

/* Default GPIO pins for SPI */
#define SPI_GPIO_CS     8   
#define SPI_GPIO_CLK    12
#define SPI_GPIO_RXD    11
#define SPI_GPIO_TXD    14

#define SPI_ISR_STATUS_RXNCIP   (0x1 << 31)
#define SPI_ISR_STATUS_RXECIP   (0x1 << 30)
#define SPI_ISR_STATUS_RXNRIP   (0x1 << 29)
#define SPI_ISR_STATUS_RXCAIP   (0x1 << 28)
#define SPI_ISR_STATUS_RXPCIP   (0x1 << 27)
#define SPI_ISR_STATUS_RXFOFIP   (0x1 << 26)
#define SPI_ISR_STATUS_RXFSRIP   (0x1 << 25)

#define SPI_ISR_STATUS_TXNCIP   (0x1 << 24)
#define SPI_ISR_STATUS_TXECIP   (0x1 << 23)
#define SPI_ISR_STATUS_TXNRIP   (0x1 << 22)
#define SPI_ISR_STATUS_TXCAIP   (0x1 << 21)
#define SPI_ISR_STATUS_TXFUFIP   (0x1 << 20)
#define SPI_ISR_STATUS_TXFSRIP   (0x1 << 19)

#define SPI_DEVICE_ISR_RX_ERR_EVENTS       (SPI_ISR_STATUS_RXECIP | SPI_ISR_STATUS_RXCAIP | SPI_ISR_STATUS_RXPCIP | SPI_ISR_STATUS_RXPCIP | SPI_ISR_STATUS_RXFOFIP | SPI_ISR_STATUS_RXFSRIP) 
#define SPI_DEVICE_ISR_TX_ERR_EVENTS       (SPI_ISR_STATUS_TXECIP | SPI_ISR_STATUS_TXCAIP | SPI_ISR_STATUS_TXFUFIP | SPI_ISR_STATUS_TXFSRIP) 

#define SPI_DEVICE_ISR_TX_EVENTS   (SPI_ISR_STATUS_TXNCIP | SPI_DEVICE_ISR_TX_ERR_EVENTS) 
#define SPI_DEVICE_ISR_RX_EVENTS   (SPI_ISR_STATUS_RXNCIP | SPI_DEVICE_ISR_RX_ERR_EVENTS) 

#define SPI_DEVICE_ISR_EVENTS   0xFFFFFFFF

#define SPI_DMA_RX_NCIP_MASK    0x80000000
#define SPI_DMA_RX_ECIP_MASK    0x40000000
#define SPI_DMA_RX_NRIP_MASK    0x20000000
#define SPI_DMA_RX_CAIP_MASK    0x10000000
#define SPI_DMA_RX_FOFIP_MASK   0x04000000

#define SPI_DMA_TX_NCIP_MASK    0x01000000
#define SPI_DMA_TX_ECIP_MASK    0x00800000
#define SPI_DMA_TX_NRIP_MASK    0x00400000
#define SPI_DMA_TX_CAIP_MASK    0x00200000

#define SPI_DMA_RX_PBUSY_MASK (0x1 << 15)
#define SPI_DMA_TX_PBUSY_MASK (0x1 << 12)
#define SPI_DMA_RX_FIFO_EMPTY_MASK (0x1 << 13)
#define SPI_DMA_TX_FIFO_EMPTY_MASK (0x1 << 10)


#define SPI_MIN_DIVISOR_VALUE           9   /* SPI minimum divisor value for clock genertaion */
#define SPI_RECV_BUFFER_ALIGN           4   /* 32-bit align */

#define SPI_DISABLED                    0
#define SPI_ENABLED                     1

#define SPI_DMA_CPU_CONTROLLED          0
#define SPI_DMA_FLEXIBLE_IO_CONTROLLED  1
#define SPI_DMA_MODE                    0
#define SPI_DIRECT_MODE                 1
#define SPI_ONE_RXBYTE                  1
#define SPI_FOUR_RXBYTE                 0

#define NARM_IO_MOD_DMA_CONTROL_ce      31, 31
#define NARM_IO_MOD_DMA_CONTROL_ca      30, 30
#define NARM_IO_MOD_DMA_CONTROL_flexio  29, 29
#define NARM_IO_MOD_DMA_CONTROL_direct  28, 28
#define NARM_IO_MOD_DMA_CONTROL_index   9, 0

#define NARM_SPI_MOD_all                    31, 0
//#define NARM_IO_MOD_DMA_CONFIG_IRQ_BITS     0x01E00000
#define NARM_IO_MOD_RX_DMA_CONFIG_IRQ_BITS     0x01A00000
#define NARM_IO_MOD_TX_DMA_CONFIG_IRQ_BITS     0x01A00000

#define NARM_SPI_IRQ_STATUS_REG     (unsigned long volatile *)0x90030000

#define NARM_SPI_DMA_RX_CONTROL_REG (unsigned long volatile *)0x90030004
#define NARM_SPI_DMA_RX_DESC_REG    (unsigned long volatile *)0x90030008
#define NARM_SPI_RX_CONFIG_REG      (unsigned long volatile *)0x9003000C

#define NARM_SPI_DMA_TX_CONTROL_REG (unsigned long volatile *)0x90030018
#define NARM_SPI_DMA_TX_DESC_REG    (unsigned long volatile *)0x9003001C
#define NARM_SPI_TX_CONFIG_REG      (unsigned long volatile *)0x90030020

#define NARM_SPI_RX_STATUS_FIFO_REG (unsigned long volatile *)0x90030010
#define NARM_SPI_RX_FIFO_REG        (unsigned long volatile *)0x90030014

#define NARM_SPI_CONFIG_REG         (unsigned long volatile *)0x90031000
#define NARM_SPI_RESET_REG          (unsigned long volatile *)0xA0900180

#define NARM_SPI_ENABLE_REG         (unsigned long volatile *)0x90031020
#define NARM_SPI_STATUS_REG         (unsigned long volatile *)0x90031024

#define NARM_SPI_CONFIG_mode                5, 4
#define NARM_SPI_CONFIG_bitOrder            2, 2
#define NARM_SPI_CONFIG_slave               1, 1
#define NARM_SPI_CONFIG_master              0, 0
#define NARM_SPI_CONFIG_rxbyte              3, 3
#define NARM_SPI_CONFIG_discard             11, 8
#define NARM_SPI_CONFIG_mlb                 12, 12

#define NARM_SPI_CLK_GEN_REG        (unsigned long volatile *)0x90031010
#define NARM_SPI_CLK_GEN_enable             16, 16
#define NARM_SPI_CLK_GEN_divisor             9, 0


#define SPI_RESET   (0x1 << 5)


/* this lock is used when updating the gSpiSlave and any shared functions */
static TX_MUTEX     gSpiSlaveMutex;

#define SPI_SLAVE_CREATE_LOCK      tx_mutex_create(&gSpiSlaveMutex, "SPI Slave Mutex", TX_INHERIT)
#define SPI_SLAVE_DELETE_LOCK      tx_mutex_delete(&gSpiSlaveMutex)
#define SPI_SLAVE_LOCK             tx_mutex_get(&gSpiSlaveMutex, TX_WAIT_FOREVER)
#define SPI_SLAVE_UNLOCK           tx_mutex_put(&gSpiSlaveMutex)

/* we only allow to handle 1 transfer at a time */
#define SPI_SLAVE_DMA_DESCRIPTORS_COUNT 1

#if (LOCAL_SPI_PORT_SLAVE == BSP_SPI_SLAVE)

/* DMA tx and rx descriptors */
#ifdef NETOS_GNU_TOOLS
    static NA_DMA_DESCRIPTOR_TYPE spi_slave_tx_buffer_descriptors[SPI_SLAVE_DMA_DESCRIPTORS_COUNT] __attribute__ ((aligned(32)));
    static NA_DMA_DESCRIPTOR_TYPE spi_slave_rx_buffer_descriptors[SPI_SLAVE_DMA_DESCRIPTORS_COUNT] __attribute__ ((aligned(32)));
#else
#pragma alignvar (32)
    static NA_DMA_DESCRIPTOR_TYPE spi_slave_tx_buffer_descriptors[SPI_SLAVE_DMA_DESCRIPTORS_COUNT];
#pragma alignvar (32)
    static NA_DMA_DESCRIPTOR_TYPE spi_slave_rx_buffer_descriptors[SPI_SLAVE_DMA_DESCRIPTORS_COUNT];
#endif

/* This is used to save the devicePtr and user buffer that has been DMAed and waiting for Master to start the transfer */
    static NaSpiSlaveBufferList_t spi_buffer_list[SPI_SLAVE_DMA_DESCRIPTORS_COUNT];

#ifdef _INTERRUPT_PROCESS
/* This is used to save all devicePtr and user buffers waiting Slave to set to the DMA */
    static NaSpiSlaveBufferList_t spi_inactive_buffer_list[NASPI_MAX_SLAVE_DMA_DESCRIPTORS];
#endif

#endif /* (LOCAL_SPI_PORT_SLAVE == BSP_SPI_SLAVE) */

/* SPI Slave Data */
static NaSpiSlave_t *gSpiSlave = NULL;

#ifdef _DEBUG_LOG_
struct spi_debug_log_t {
    unsigned isr_error_status[100];        
    unsigned isr_error_status_count;
    unsigned isr_unknown_count[2];
    unsigned isr_status[100];
    unsigned isr_status_count;
    unsigned isr_add_buffer_error_count;
    unsigned isr_dequeue_buffer_error_count;
    unsigned rx_buffer_missing_count;
    unsigned tx_buffer_missing_count;
    unsigned tx_process_not_done_count;
    unsigned rx_process_not_done_count;
};

static struct spi_debug_log_t gSpiSlaveLog;
#endif

#ifdef _INTERRUPT_PROCESS
/* save user buffer in the inactive list
*/
static int queue_inactive_buffer(NaSpiSlaveDevice_t * devicePtr, NASpiSlaveBufferType * user_buffer)
{
    unsigned i;
    int rc = -1;
    NaSpiSlaveBufferList_t * listBuffer;

    SPI_SLAVE_LOCK;
    i = gSpiSlave->user_inactive_buffer_count;

    listBuffer = &gSpiSlave->user_inactive_buffer_list[i];
    listBuffer->device = devicePtr;
    listBuffer->user_buffer = user_buffer;
    listBuffer->readBuffer = user_buffer->readBuffer;
    listBuffer->writeBuffer = user_buffer->writeBuffer;
    listBuffer->in_used = 1;
    rc = 0;

    i++;
    if (i == NASPI_MAX_SLAVE_DMA_DESCRIPTORS)
    {
        i = 0;
    }
    gSpiSlave->user_inactive_buffer_count = i;

    SPI_SLAVE_UNLOCK;
    return rc;
}

/* get user buffer from the inactive list
*/
static NaSpiSlaveBufferList_t * dequeue_inactive_buffer(void)
{
    unsigned i;
    NaSpiSlaveBufferList_t * listBuffer = NULL;

    SPI_SLAVE_LOCK;

    i = gSpiSlave->user_inactive_buffer_index;
     
        if (gSpiSlave->user_inactive_buffer_list[i].in_used == 1)
        {
            listBuffer = &gSpiSlave->user_inactive_buffer_list[i];
            listBuffer->in_used = 0;
            i++;
            if (i == NASPI_MAX_SLAVE_DMA_DESCRIPTORS)
            {
                i = 0;
            }
            gSpiSlave->user_inactive_buffer_index = i;
        }

    SPI_SLAVE_UNLOCK;

    return listBuffer;
}
#endif /*  _INTERRUPT_PROCESS */

/* save the user buffer in active list
 */
static int add_spi_buffer(NaSpiSlaveDevice_t * devicePtr, NASpiSlaveBufferType * user_buffer)
{
    unsigned i;
    int rc = -1;
    NaSpiSlaveBufferList_t * listBuffer;

    SPI_SLAVE_LOCK;
    for (i=0; i < gSpiSlave->user_buffer_count; i++)
    {
        listBuffer = &gSpiSlave->user_buffer_list[i];
        if (listBuffer->in_used == 0)
        {
            listBuffer->device = devicePtr;
            listBuffer->user_buffer = user_buffer;
            listBuffer->readBuffer = user_buffer->readBuffer;
            listBuffer->writeBuffer = user_buffer->writeBuffer;
            listBuffer->in_used = 1;
#ifdef _DEBUG_PRINTF
            printf("add_spi_buffer: %d - 0x%X write 0x%X read 0x%X --->\n", i, (unsigned)listBuffer, 
                            (unsigned)listBuffer->writeBuffer, (unsigned)listBuffer->readBuffer);
#endif
            rc = 0;
            break;
        }
    }
    SPI_SLAVE_UNLOCK;
    return rc;
}

/* get user buffer from active list 
 */
static NaSpiSlaveBufferList_t * get_spi_buffer(char * writeBuffer, char * readBuffer)
{
    unsigned i;
    NaSpiSlaveBufferList_t * listBuffer = NULL;

    SPI_SLAVE_LOCK;
    for (i=0; i < gSpiSlave->user_buffer_count; i++)
    {
        if (gSpiSlave->user_buffer_list[i].in_used == 1)
        {
            /* Let's look for write buffer */
            if (writeBuffer != NULL && gSpiSlave->user_buffer_list[i].writeBuffer == writeBuffer)
            {
                listBuffer = &gSpiSlave->user_buffer_list[i];
#ifdef _DEBUG_PRINTF
                printf("get_spi_buffer: %d - 0x%X write 0x%X\n", i, (unsigned)listBuffer, (unsigned)writeBuffer);
                if (listBuffer->writeBuffer != gSpiSlave->user_buffer_list[i].user_buffer->writeBuffer)
                {
                    printf("get_spi_buffer: ERROR write buffer 0x%X\n", (unsigned)gSpiSlave->user_buffer_list[i].user_buffer->writeBuffer);
                }
#endif
                break;
            }

            /* Let's look for read buffer */
            if (readBuffer != NULL && gSpiSlave->user_buffer_list[i].readBuffer == readBuffer)
            {
                listBuffer = &gSpiSlave->user_buffer_list[i];
#ifdef _DEBUG_PRINTF
                printf("get_spi_buffer: %d - 0x%X read 0x%X\n", i, (unsigned)listBuffer, (unsigned)readBuffer);
                if (listBuffer->readBuffer != gSpiSlave->user_buffer_list[i].user_buffer->readBuffer)
                {
                    printf("get_spi_buffer: ERROR read buffer 0x%X\n", (unsigned)gSpiSlave->user_buffer_list[i].user_buffer->readBuffer);
                }
#endif
                break;
            }
        }
    }

    SPI_SLAVE_UNLOCK;

    return listBuffer;
}
/* free active list
 */
static int free_spi_buffer(char * writeBuffer, char * readBuffer)
{
    unsigned i;
    int rc = -1;
    NaSpiSlaveBufferList_t * listBuffer;

    SPI_SLAVE_LOCK;
    for (i=0; i < gSpiSlave->user_buffer_count; i++)
    {
        listBuffer = &gSpiSlave->user_buffer_list[i];
        if (listBuffer->in_used == 1)
        {
            /* Let's look for write buffer */
            if (writeBuffer != NULL && listBuffer->writeBuffer == writeBuffer)
            {
#ifdef _DEBUG_PRINTF
                printf("free_spi_buffer: %d - <---- 0x%X write 0x%X\n", i, (unsigned) listBuffer, (unsigned) writeBuffer);
#endif
                listBuffer->writeBuffer = NULL;
            }

            /* Let's look for read buffer */
            if (readBuffer != NULL && listBuffer->readBuffer == readBuffer)
            {
#ifdef _DEBUG_PRINTF
                printf("free_spi_buffer: %d - <---- 0x%X read 0x%X \n", i, (unsigned) listBuffer, (unsigned)readBuffer);
#endif
                listBuffer->readBuffer = NULL;
            }

            if (listBuffer->writeBuffer == NULL && listBuffer->readBuffer == NULL)
            {
                listBuffer->in_used = 0;
                listBuffer->device = NULL;
                listBuffer->user_buffer = NULL;
                rc = 0;
                break;
            }
        }
    }

    SPI_SLAVE_UNLOCK;

    return rc;
}
/* reset SPI
 */
static void spi_reset(void)
{
    narm_write_reg(NA_SCM_RSR_REG, NA_SCM_RSR_ADDRESS, spi, SCM_RSR_RESET_MODULE);
/*    NAuWait(100); */
    narm_write_reg(NA_SCM_RSR_REG, NA_SCM_RSR_ADDRESS, spi, SCM_RSR_ENABLE_MODULE);
}

/*
 * Set up SPI register for SPI slave with given mode and bit order.
 */
static void spi_hardware_setup(NASpiMode mode, NASpiBitOrder bitOrder)
{
#define _SPI_SLAVE_DIVISOR 0x006 /* a fix value for slave */

    int divisor;

    /* setup SPI configuration register */
//    narm_write_reg (NARM_SPI_CONFIG,  NARM_SPI_CONFIG_REG, mlb, SPI_ENABLED); 
    narm_write_reg (NARM_SPI_CONFIG,  NARM_SPI_CONFIG_REG, mode, mode); 
    narm_write_reg (NARM_SPI_CONFIG,  NARM_SPI_CONFIG_REG, bitOrder, bitOrder); 
    narm_write_reg (NARM_SPI_CONFIG,  NARM_SPI_CONFIG_REG, rxbyte, SPI_ONE_RXBYTE); 
    narm_write_reg (NARM_SPI_CONFIG,  NARM_SPI_CONFIG_REG, slave, SPI_ENABLED); 
    narm_write_reg (NARM_SPI_CONFIG,  NARM_SPI_CONFIG_REG, master, SPI_DISABLED); 

    /* Setup SPI Clock Generation */
    divisor = _SPI_SLAVE_DIVISOR;

    /* set up divisor */
    narm_write_reg (NARM_SPI_CLK_GEN,  NARM_SPI_CLK_GEN_REG, enable, SPI_DISABLED); 
    narm_write_reg (NARM_SPI_CLK_GEN,  NARM_SPI_CLK_GEN_REG, divisor, divisor); 
    narm_write_reg (NARM_SPI_CLK_GEN,  NARM_SPI_CLK_GEN_REG, enable, SPI_ENABLED); 
}

/*
 * Set up DMA register for SPI
 */
static void dma_hardware_setup(void)
{
    /* setup SPI DMA for Rx */
    narm_write_reg (NARM_SPI_MOD, NARM_SPI_DMA_RX_CONTROL_REG, all, 0);
    narm_write_reg (NARM_IO_MOD_DMA_CONTROL, NARM_SPI_DMA_RX_CONTROL_REG, ce, SPI_DISABLED);      
    narm_write_reg (NARM_IO_MOD_DMA_CONTROL, NARM_SPI_DMA_RX_CONTROL_REG, flexio, SPI_DMA_CPU_CONTROLLED);       
    narm_write_reg (NARM_IO_MOD_DMA_CONTROL, NARM_SPI_DMA_RX_CONTROL_REG, direct, SPI_DMA_MODE); 
    /* enable irq bits for Rx */
    narm_write_reg (NARM_SPI_MOD,  NARM_SPI_RX_CONFIG_REG, all, NARM_IO_MOD_RX_DMA_CONFIG_IRQ_BITS); 

    /* setup SPI DMA for Tx */
    narm_write_reg (NARM_SPI_MOD, NARM_SPI_DMA_TX_CONTROL_REG, all, 0);
    narm_write_reg (NARM_IO_MOD_DMA_CONTROL, NARM_SPI_DMA_TX_CONTROL_REG, ce, SPI_DISABLED);      
    narm_write_reg (NARM_IO_MOD_DMA_CONTROL, NARM_SPI_DMA_TX_CONTROL_REG, flexio, SPI_DMA_CPU_CONTROLLED);       
    narm_write_reg (NARM_IO_MOD_DMA_CONTROL, NARM_SPI_DMA_TX_CONTROL_REG, direct, SPI_DMA_MODE); 
    /* enable irq bits for Tx */
    narm_write_reg (NARM_SPI_MOD,  NARM_SPI_TX_CONFIG_REG, all, NARM_IO_MOD_TX_DMA_CONFIG_IRQ_BITS); 

    /* setup SPI Configuration */
    narm_write_reg (NARM_SPI_MOD,  NARM_SPI_CONFIG_REG, all, 0); 
}

/*
 * Sets up DMA buffer descriptors.
 */
static int dma_descriptor_setup(NaSpiSlaveDevice_t * devicePtr, char *const writeBuffer, char *readBuffer, int bufferLength)
{
    int rc = -1;
    int descIndex;

    physical_address_t      physTx;
    NA_DMA_DESCRIPTOR_TYPE  *txDescPtr;
    unsigned long           txFlag;

    physical_address_t      physRx;
    NA_DMA_DESCRIPTOR_TYPE  *rxDescPtr;
    unsigned long           rxFlag;

    /*
     * setup rx buffer descriptor
     * Make sure we have dma buffer descriptor available before setting the buffer.
     */
    if (readBuffer != NULL && gSpiSlave->setup_rx_descriptor_index < gSpiSlave->max_descriptor_count)
    {
        /* Get physical address for dma descriptor */
        if (!NAVaToPhys ((const void *) readBuffer, &physRx))
            netosFatalError ("spi rx DMA Cache error", 1, 1);

        NABeforeDMA (NA_DEVICE_TO_MEMORY_DMA, NULL, readBuffer, bufferLength);

        /* setting rx dma buffer descriptor */
        descIndex = gSpiSlave->setup_rx_descriptor_index;

        rxDescPtr = &gSpiSlave->rx_buffer_descriptors[descIndex];
        
        rxDescPtr->src_addr = physRx;
        rxDescPtr->dst_addr = 0;
        rxDescPtr->buf_len = bufferLength;
        rxFlag = DMA_INTERRUPT_MASK | DMA_LAST_MASK;

        /* increment the device rx descriptor counter so
         * we keep track number of rx descriptors is being used by this device.
         */
        devicePtr->rx_descriptor_count++;

        /* increment the total number of SPI slave rx descriptors. */
        gSpiSlave->setup_rx_descriptor_index++;

        /* need to set WRAP bit if we reach the max dma descriptor */
        if (gSpiSlave->setup_rx_descriptor_index == gSpiSlave->max_descriptor_count) 
        {
            gSpiSlave->setup_rx_descriptor_index = 0;

            rxFlag |= DMA_WRAP_MASK;
        }
        
        rxDescPtr->flag = rxFlag;

        rc = 0;
    }
    else if (gSpiSlave->setup_rx_descriptor_index > gSpiSlave->max_descriptor_count)
    {   /* we don't have dma descriptor to set this buffer */
/*        printf("setup_dma: rx dma is full %d\n", gSpiSlave->setup_rx_descriptor_index); */
        rc = -1;
        goto _ret;
    }
    
    /*
     * setup tx buffer descriptor
     * Make sure we have dma buffer descriptor available before setting the buffer.
     */
    if (writeBuffer != NULL && gSpiSlave->setup_tx_descriptor_index < gSpiSlave->max_descriptor_count)
    {
/*        printf("setup_dma: %d - TX 0x%X length 0x%x\n", gSpiSlave->setup_tx_descriptor_index, (unsigned)writeBuffer, bufferLength); */

        /* Get physical address for dma descriptor */
        if (!NAVaToPhys ((const void *) writeBuffer, &physTx))
            netosFatalError ("spi tx DMA Cache error", 1, 1);

        NABeforeDMA (NA_MEMORY_TO_DEVICE_DMA, writeBuffer, NULL, bufferLength);

        descIndex = gSpiSlave->setup_tx_descriptor_index;

        txDescPtr = &gSpiSlave->tx_buffer_descriptors[descIndex];
        txDescPtr->src_addr = physTx;
        txDescPtr->dst_addr = 0;
        txDescPtr->buf_len = bufferLength;
        txFlag = DMA_INTERRUPT_MASK | DMA_LAST_MASK | DMA_FULL_MASK;

        /* increment the device tx descriptor counter so
         * we keep track number of tx descriptors is being used by this device.
         */
        devicePtr->tx_descriptor_count++; 

        /* increment the total number of SPI slave tx descriptors. */
        gSpiSlave->setup_tx_descriptor_index++;

        /* need to set WRAP bit if we reach the max dma descriptor */
        if (gSpiSlave->setup_tx_descriptor_index == gSpiSlave->max_descriptor_count) 
        {
            gSpiSlave->setup_tx_descriptor_index = 0;

            txFlag |= DMA_WRAP_MASK;
        }
        txDescPtr->flag = txFlag;
        rc = 0;

    }
    else if (gSpiSlave->setup_tx_descriptor_index > gSpiSlave->max_descriptor_count)
    {   /* we don't have dma descriptor to set this buffer */
/*        printf("setup_dma: tx dma is full %d\n", gSpiSlave->setup_tx_descriptor_index); */
        rc = -1;
    }

_ret:
    return rc;
}

/*
 * This routine enables SPI RX DMA by toggling the CE bit
 */
static void enable_rx_spi(void)
{
        narm_write_reg (NARM_IO_MOD_DMA_CONTROL, NARM_SPI_DMA_RX_CONTROL_REG, ca, SPI_DISABLED);
    /* enable SPI DMA by toggle the CE bit */
        narm_write_reg (NARM_IO_MOD_DMA_CONTROL, NARM_SPI_DMA_RX_CONTROL_REG, ce, SPI_DISABLED);      
        narm_write_reg (NARM_IO_MOD_DMA_CONTROL, NARM_SPI_DMA_RX_CONTROL_REG, ce, SPI_ENABLED);      
}
/*
 * This routine enables SPI TX DMA by toggling the CE bit
 */
static void enable_tx_spi(void)
{
        narm_write_reg (NARM_IO_MOD_DMA_CONTROL, NARM_SPI_DMA_TX_CONTROL_REG, ca, SPI_DISABLED);      
    /* enable SPI DMA by toggle the CE bit */
        narm_write_reg (NARM_IO_MOD_DMA_CONTROL, NARM_SPI_DMA_TX_CONTROL_REG, ce, SPI_DISABLED);      
        narm_write_reg (NARM_IO_MOD_DMA_CONTROL, NARM_SPI_DMA_TX_CONTROL_REG, ce, SPI_ENABLED);      
}

/*
 * This routine enables SPI TX DMA by toggling the CE bit
 */
#if 0
static void disable_tx_spi(void)
{

    /* enable SPI DMA by toggle the CE bit */
        narm_write_reg (NARM_IO_MOD_DMA_CONTROL, NARM_SPI_DMA_TX_CONTROL_REG, ce, SPI_DISABLED);      
}
static void disable_rx_spi(void)
{
    /* enable SPI DMA by toggle the CE bit */
        narm_write_reg (NARM_IO_MOD_DMA_CONTROL, NARM_SPI_DMA_RX_CONTROL_REG, ce, SPI_DISABLED);      
}
#endif

/*
 * Process TX DMA descriptor.
 * It processes all TX DMA descriptors if it's done transmitting with FULL bit clear.
 * It will call user's callback function with buffer status indicating SPI Slave is done
 * with this tx buffer.
 */
static void process_tx_dma(void)
{
    NASpiSlaveBufferType    * user_buffer;
    NA_DMA_DESCRIPTOR_TYPE  * descPtr;
    NaSpiSlaveDevice_t      * devicePtr;
    int                     descIndex;
    physical_address_t      phys;
    unsigned long           status = NASPI_SLAVE_DMA_SUCCESS;
    NaSpiSlaveBufferList_t  * listBuffer;
#ifndef _INTERRUPT_PROCESS
    unsigned long       queue_msg[2];
#endif

    descIndex = gSpiSlave->process_tx_descriptor_index;

    /* Make sure DMA controller is done with the buffer descriptors.
     * Check the DMA_FULL_MASK bit.
     * We have saved all user's buffers in a buffer list, we need
     * to get the buffer list according to the dma buffer address.
     * This tells us the device and user's callback function for
     * this dma buffer address.
     */
//    while (gSpiSlave->tx_buffer_descriptors[descIndex].src_addr != 0 && (gSpiSlave->tx_buffer_descriptors[descIndex].flag & DMA_FULL_MASK) != DMA_FULL_MASK)
#ifdef _DEBUG_LOG_
    if (gSpiSlave->tx_buffer_descriptors[descIndex].src_addr != 0 && (gSpiSlave->tx_buffer_descriptors[descIndex].flag & DMA_FULL_MASK) == DMA_FULL_MASK)
    {
        gSpiSlaveLog.tx_process_not_done_count++;
    }
#endif
    {

        descPtr = &gSpiSlave->tx_buffer_descriptors[descIndex];

        /* locate the buffer list */
        listBuffer = get_spi_buffer((char *)descPtr->src_addr, NULL);
        if (listBuffer != NULL)
        {
            SPI_SLAVE_LOCK;
            gSpiSlave->dma_tx_start = 0;        /* indicating we'er done process the dma */
            gSpiSlave->tx_descriptor_count--;   /* decrement the tx count */
            SPI_SLAVE_UNLOCK;

            /* get the user's buffer and device */
            devicePtr = listBuffer->device;
            user_buffer = listBuffer->user_buffer;

            if (devicePtr == NULL || user_buffer == NULL)
                netosFatalError ("process_tx_dma: spi user buffer NULL", 1, 1);


            /* We are going to check whether user's tx buffer is same as the dma buffer addr
             * in the DMA.
             *
             * If user tx buffer is NULL (not dma buffer addr in the DMA), set BUFFER ERROR.
             * Other module may set the writeBuffer to NULL on this user_buffer.
             *
             * If user's tx buffer is not same as dma buffer addr in the DMA, set MISMATCH ERROR.
             * Other module may set the writeBuffer to different buffer pointer in this user_buffer.
             */
            if (user_buffer->writeBuffer != NULL)
            {
                if (!NAVaToPhys ((const void *) user_buffer->writeBuffer, &phys))
                    netosFatalError ("process_tx_dma: spi tx DMA Cache error", 1, 1);

                /* the tx buffer in the DMA and the user's tx buffer should be same */
                if (descPtr->src_addr != (unsigned long)phys)
                {
                    status = NASPI_SLAVE_DMA_MISMATCH_ERROR;
                }
            }
            else
            {
                status = NASPI_SLAVE_DMA_BUFFER_ERROR;
            }

            SPI_SLAVE_LOCK;
            devicePtr->tx_descriptor_count--;  /* decrement the device tx buffer count */
            SPI_SLAVE_UNLOCK;

            /* set the status and the number of bytes sent */
            user_buffer->status = status;
            user_buffer->byteSent = user_buffer->bufferLength - (descPtr->buf_len & 0xFFFF);
            /* now clear the src_addr to indicate we are finishing process this DMA descriptor */
            descPtr->src_addr = 0;

            NAAfterDMA (NA_MEMORY_TO_DEVICE_DMA, user_buffer->writeBuffer, NULL, user_buffer->byteSent);

            /* free the buffer list.
             * The buffer list entry will be freed if both write and read buffers are freed.
             * So we should free the write buffer once we are done here.
             * 
             * If we get error on the write buffer, we should also free read buffer so
             * the buffer list entry is freed.
             */
            free_spi_buffer(user_buffer->writeBuffer, NULL);

            /* if we get error, free the buffer list */
            if (user_buffer->status != NASPI_SLAVE_DMA_SUCCESS && user_buffer->readBuffer != NULL)
            {
                free_spi_buffer(NULL, user_buffer->readBuffer);
            }

    		/* call user callback functions if we don't have readBuffer to process or error status.
             * We only call the user's callback function once for each NASpiSlaveEnqueueBuffer call.
             * If we finish this write buffer but not read buffer, we don't call the callback until
             * read buffer is processed. 
    		*/
            if (user_buffer->bufferReturnFn != NULL && (listBuffer->readBuffer == NULL || user_buffer->status != NASPI_SLAVE_DMA_SUCCESS))
            {
#ifdef _INTERRUPT_PROCESS
        		(user_buffer->bufferReturnFn)(user_buffer);
#else
                queue_msg[0] = (unsigned long)devicePtr;
                queue_msg[1] = (unsigned long)user_buffer;

                if (tx_queue_send(&gSpiSlave->done_queue, (void *)&queue_msg, TX_NO_WAIT) != TX_SUCCESS)
                    netosFatalError ("process_tx_dma: spi tx DMA Cache error", 1, 1);
#endif

            }

        }

        else
        {
            /* still need to free and clean up the descriptor */
            descPtr->src_addr = 0;
            SPI_SLAVE_LOCK;
            gSpiSlave->dma_tx_start = 0;        /* indicating we'er done process the dma */
            gSpiSlave->tx_descriptor_count--;  /* decrement the tx count */
            SPI_SLAVE_UNLOCK;
#ifdef _DEBUG_LOG_
            gSpiSlaveLog.tx_buffer_missing_count++;
#endif
        }
        /* next DMA descriptor */
        descIndex++;
        if (descIndex == gSpiSlave->max_descriptor_count)
            descIndex = 0;

    }
    /* set the DMA descriptor that we are going to process */
    gSpiSlave->process_tx_descriptor_index = descIndex;
}

/*
 * Process RX DMA descriptor.
 * It processes all RX DMA descriptors if it receives data (The DMA will set FULL bit).
 * It will call user's callback function with buffer status indicating SPI Slave is done
 * with this rx buffer.
 */
static void process_rx_dma(void)
{
    NASpiSlaveBufferType    * user_buffer;
    NA_DMA_DESCRIPTOR_TYPE  * descPtr;
    NaSpiSlaveDevice_t      * devicePtr;
    int                     descIndex;
    physical_address_t      phys;
    unsigned long           status = NASPI_SLAVE_DMA_SUCCESS;
    NaSpiSlaveBufferList_t  * listBuffer;
#ifndef _INTERRUPT_PROCESS
    unsigned long       queue_msg[2];
#endif
    descIndex = gSpiSlave->process_rx_descriptor_index;


    /* Make sure DMA controller is done with the buffer descriptors.
     * Check the DMA_FULL_MASK bit.
     * We have saved all user's buffers in a buffer list, we need
     * to get the buffer list according to the dma buffer address.
     * This tells us the device and user's callback function for
     * this dma buffer address.
     */
//    while (gSpiSlave->rx_buffer_descriptors[descIndex].src_addr != 0 && (gSpiSlave->rx_buffer_descriptors[descIndex].flag & DMA_FULL_MASK) == DMA_FULL_MASK)
#ifdef _DEBUG_LOG_
    if (gSpiSlave->rx_buffer_descriptors[descIndex].src_addr != 0 && (gSpiSlave->rx_buffer_descriptors[descIndex].flag & DMA_FULL_MASK) != DMA_FULL_MASK)
    {
        gSpiSlaveLog.rx_process_not_done_count++;
    }
#endif
    {

        descPtr = &gSpiSlave->rx_buffer_descriptors[descIndex];

        listBuffer = get_spi_buffer(NULL, (char *)descPtr->src_addr);
        if (listBuffer != NULL)
        {
            devicePtr = listBuffer->device;
            user_buffer = listBuffer->user_buffer;

            if (devicePtr == NULL || user_buffer == NULL)
                netosFatalError ("process_rx_dma: spi user buffer NULL", 1, 1);


            SPI_SLAVE_LOCK;
            gSpiSlave->dma_rx_start = 0;        /* indicating we'er done process the dma */
            gSpiSlave->rx_descriptor_count--;  /* decrement the tx count */
            SPI_SLAVE_UNLOCK;


            /* We are going to check whether user's rx buffer is same as the dma buffer addr
             * in the DMA.
             *
             * If user rx buffer is NULL (not dma buffer addr in the DMA), set BUFFER ERROR.
             * Other module may set the writeBuffer to NULL on this user_buffer.
             *
             * If user's rx buffer is not same as dma buffer addr in the DMA, set MISMATCH ERROR.
             * Other module may set the writeBuffer to different buffer pointer in this user_buffer.
             */
            if (user_buffer->readBuffer != NULL)
            {
                if (!NAVaToPhys ((const void *) user_buffer->readBuffer, &phys))
                    netosFatalError ("process_rx_dma: spi rx DMA Cache error", 1, 1);

                /* checking RX descriptor and make sure DMA controller is done */
                if (descPtr->src_addr != (unsigned long)phys)
                {
                    status = NASPI_SLAVE_DMA_MISMATCH_ERROR;
                }
            }
            else
            {
                status = NASPI_SLAVE_DMA_BUFFER_ERROR;
            }

            SPI_SLAVE_LOCK;
            devicePtr->rx_descriptor_count--;  /* decrement the device tx buffer count */
            SPI_SLAVE_UNLOCK;

            /* set the status and the number of bytes received */
            user_buffer->status = status;
            user_buffer->byteReceived = (descPtr->buf_len & 0xFFFF);
            descPtr->src_addr = 0;

            NAAfterDMA (NA_DEVICE_TO_MEMORY_DMA, NULL, user_buffer->readBuffer, user_buffer->byteReceived);

            /* free the buffer list.
             * The buffer list entry will be freed if both write and read buffers are freed.
             * So we should free the write buffer once we are done here.
             * 
             * If we get error on the write buffer, we should also free read buffer so
             * the buffer list entry is freed.
             */
            free_spi_buffer(NULL, user_buffer->readBuffer);
            if (user_buffer->status != NASPI_SLAVE_DMA_SUCCESS && user_buffer->writeBuffer != NULL)
            {
                free_spi_buffer(NULL, user_buffer->writeBuffer);
            }
    		/* call user callback functions if we don't have writeBuffer to process or error status.
             * We only call the user's callback function once for each NASpiSlaveEnqueueBuffer call.
             * If we finish this read buffer but not write buffer, we don't call the callback until
             * write buffer is processed. 
    		*/
            if (user_buffer->bufferReturnFn != NULL && (listBuffer->writeBuffer == NULL || user_buffer->status != NASPI_SLAVE_DMA_SUCCESS))
            {
#ifdef _INTERRUPT_PROCESS
        		(user_buffer->bufferReturnFn)(user_buffer);
#else
                queue_msg[0] = (unsigned long)devicePtr;
                queue_msg[1] = (unsigned long)user_buffer;

                if (tx_queue_send(&gSpiSlave->done_queue, (void *)&queue_msg, TX_NO_WAIT) != TX_SUCCESS)
                    netosFatalError ("process_tx_dma: spi tx DMA Cache error", 1, 1);
#endif

            }
#if 0
            else if (user_buffer->writeBuffer != NULL)
            {
                /* Stop the TX so that it will not continue tx data. This prevents the master is trying to
                 * get tx data less than what we set up here.
                 * We need to close the tx to complete this SPI transfer.
                 */
                narm_write_reg (NARM_IO_MOD_DMA_CONTROL, NARM_SPI_DMA_TX_CONTROL_REG, ca, SPI_ENABLED);      

            }
#endif
        }
        else 
        {
            /* still need to free and clean up the descriptor */
            descPtr->src_addr = 0;
            SPI_SLAVE_LOCK;
            gSpiSlave->dma_rx_start = 0;        /* indicating we'er done process the dma */
            gSpiSlave->rx_descriptor_count--;  /* decrement the tx count */
            SPI_SLAVE_UNLOCK;
#ifdef _DEBUG_LOG_
            gSpiSlaveLog.rx_buffer_missing_count++;
#endif
        }

        /* next DMA descriptor */
        descIndex++;
        if (descIndex == gSpiSlave->max_descriptor_count)
            descIndex = 0;
    }

    /* set the DMA descriptor that we are going to process */
    gSpiSlave->process_rx_descriptor_index = descIndex;
}


#ifndef _INTERRUPT_PROCESS
static void process_user_buffer(NaSpiSlaveDevice_t * devicePtr, NASpiSlaveBufferType * user_buffer)
{
    if (user_buffer->bufferReturnFn != NULL)
        (user_buffer->bufferReturnFn)(user_buffer);
}
/*
 * This is a spi slave thread to process DMA descriptors.
 * It waits for events from ISR or times out if we have active DMA descriptors setup.
 */
static void spi_slave_handle (unsigned long not_used)
{
#define _SPI_DEFAULT_WAIT_TIME  1

    int                     rc;
    unsigned long           queue_msg[2];
    NASpiSlaveBufferType    * user_buffer;
    NaSpiSlaveDevice_t      * devicePtr;

    TX_INTERRUPT_SAVE_AREA;

    NA_UNUSED_PARAM(not_used);

    for (;;)
    {
        /* get done queue for any user buffers that are done */
        rc = tx_queue_receive(&gSpiSlave->done_queue, &queue_msg, _SPI_DEFAULT_WAIT_TIME);

        if (rc != TX_SUCCESS && rc != TX_QUEUE_EMPTY)
        {   /* something wrong here */
#ifdef _DEBUG_PRINTF
            printf("spi_slave_handle: tx_queue_receive returns %d\n", rc);
#endif
        }
        else
        {
            if (rc != TX_QUEUE_EMPTY)
            {
                devicePtr = (NaSpiSlaveDevice_t *) queue_msg[0];
                user_buffer = (NASpiSlaveBufferType *)queue_msg[1];

        	    TX_DISABLE;
                process_user_buffer(devicePtr, user_buffer);
               	TX_RESTORE;
            }
            /* if we finish processing tx dma and we still have tx dma descriptor setup,
             * restart tx dma
             */
        }

        if (gSpiSlave->dma_tx_start == 0 && gSpiSlave->dma_rx_start == 0)
        {
        	TX_DISABLE;
            rc = tx_queue_receive(&gSpiSlave->queue, &queue_msg, TX_NO_WAIT);
            if (rc == TX_SUCCESS)
            {
                devicePtr = (NaSpiSlaveDevice_t *) queue_msg[0];
                user_buffer = (NASpiSlaveBufferType *)queue_msg[1];
                rc = add_spi_buffer(devicePtr, user_buffer);
                if (rc != 0)
                {
                    printf("spi_slave_handle: Something wrong !!!!!!\n");
                }
                printf("spi_slave_handle: starts SPI...\n");
                spi_reset();
//                dma_hardware_setup();
                spi_hardware_setup(devicePtr->spiMode, devicePtr->bitOrder);
                dma_descriptor_setup(devicePtr, user_buffer->writeBuffer, user_buffer->readBuffer, user_buffer->bufferLength);

                if (user_buffer->writeBuffer != NULL)
                {
                    gSpiSlave->dma_tx_start = 1;
                    enable_tx_spi();
                }

                /* if we finish processing rx dma and we still have rx dma descriptor setup,
                 * restart rx dma
                 */
                if (user_buffer->readBuffer != NULL)
                {
                    gSpiSlave->dma_rx_start = 1;
                    enable_rx_spi();
                }
            }
           	TX_RESTORE;
        }
    }
}
#endif
/*
 * This is SPI slave interrupt service routine. 
 * It reads the interrupt status and sets an event to invoke the spi slave handle thread.
 */
static void spi_slave_isr(int isr_num)
{
#ifdef _DEBUG_LOG_
    unsigned                dma_index;
#endif

#ifdef _INTERRUPT_PROCESS
    NASpiSlaveBufferType    * user_buffer;
    NaSpiSlaveDevice_t      * devicePtr;
    NaSpiSlaveBufferList_t  * list_buffer;
#endif
    unsigned long status;

    if (isr_num != SPI_INTERRUPT)
    {
#ifdef _DEBUG_LOG_
        gSpiSlaveLog.isr_unknown_count[0]++;
#endif
        return;
    }

    status = *NARM_SPI_IRQ_STATUS_REG;

    /* Acknowledge interrupt */
    *NARM_SPI_IRQ_STATUS_REG = status; 

#ifdef _DEBUG_LOG_
    gSpiSlaveLog.isr_status[gSpiSlaveLog.isr_status_count] = status;
    gSpiSlaveLog.isr_status_count++;
    if (gSpiSlaveLog.isr_status_count == 100) gSpiSlaveLog.isr_status_count = 0;
#endif

    if (gSpiSlave != NULL)
    {

    /* See whether we get error event from ISR */
    if ((status & SPI_DEVICE_ISR_TX_ERR_EVENTS) != 0)
    {
#ifdef _DEBUG_LOG_
        dma_index = narm_read_reg(NARM_IO_MOD_DMA_CONTROL, NARM_SPI_DMA_TX_CONTROL_REG, index);
        gSpiSlaveLog.isr_error_status[gSpiSlaveLog.isr_error_status_count] = dma_index;
        gSpiSlaveLog.isr_error_status_count++;
        if (gSpiSlaveLog.isr_error_status_count == 100) gSpiSlaveLog.isr_error_status_count = 0;
#endif
    }
    else if ((status & SPI_DEVICE_ISR_RX_ERR_EVENTS) != 0)
    {
#ifdef _DEBUG_LOG_
        dma_index = narm_read_reg(NARM_IO_MOD_DMA_CONTROL, NARM_SPI_DMA_RX_CONTROL_REG, index);
        gSpiSlaveLog.isr_error_status[gSpiSlaveLog.isr_error_status_count] = dma_index;
        gSpiSlaveLog.isr_error_status_count++;
        if (gSpiSlaveLog.isr_error_status_count == 100) gSpiSlaveLog.isr_error_status_count = 0;
#endif
    }

    if ((status & SPI_DEVICE_ISR_TX_EVENTS) != 0 /*  && gSpiSlave->dma_tx_start > 0 */)
    {
//        disable_tx_spi();
        narm_write_reg (NARM_IO_MOD_DMA_CONTROL, NARM_SPI_DMA_TX_CONTROL_REG, ca, SPI_DISABLED);      
        process_tx_dma();
    }

    if ((status & SPI_DEVICE_ISR_RX_EVENTS) != 0 /* && gSpiSlave->dma_rx_start > 0 */)
    {
        narm_write_reg (NARM_IO_MOD_DMA_CONTROL, NARM_SPI_DMA_TX_CONTROL_REG, ca, SPI_ENABLED);      
//        enable_rx_spi();
//        disable_rx_spi();
//        spi_reset();
        process_rx_dma();
    }
//        tx_event_flags_set(&gSpiSlave->event, status, TX_OR);

#ifdef _INTERRUPT_PROCESS
    if (gSpiSlave->dma_tx_start == 0 && gSpiSlave->dma_rx_start == 0)
    {
        list_buffer = dequeue_inactive_buffer();
        if (list_buffer != NULL)
        {
            devicePtr = list_buffer->device;
            user_buffer = list_buffer->user_buffer;
            if (add_spi_buffer(devicePtr, user_buffer) == 0)
            {
                spi_reset();
//                    dma_hardware_setup();
                spi_hardware_setup(devicePtr->spiMode, devicePtr->bitOrder);
                dma_descriptor_setup(devicePtr, user_buffer->writeBuffer, user_buffer->readBuffer, user_buffer->bufferLength);
                if (user_buffer->writeBuffer != NULL)
                {
                    gSpiSlave->dma_tx_start = 1;
                    enable_tx_spi();
                }

                /* if we finish processing rx dma and we still have rx dma descriptor setup,
                 * restart rx dma
                 */
                if (user_buffer->readBuffer != NULL)
                {
                    gSpiSlave->dma_rx_start = 1;
                    enable_rx_spi();
                }
            }
            else
            {   /* should we stop and flag the fatal error? */
                /* netosFatalError ("spi isr add buffer error", 1, 1); */
#ifdef _DEBUG_LOG_
                gSpiSlaveLog.isr_add_buffer_error_count++;
#endif
            }
        }
        else
        {   /* should we stop and flag the fatal error? */
            /* netosFatalError ("spi isr dequeue error", 1, 1); */
#ifdef _DEBUG_LOG_
            gSpiSlaveLog.isr_dequeue_buffer_error_count++;
#endif
        }
    }
#ifdef _DEBUG_LOG_
    else
    {
        gSpiSlaveLog.isr_unknown_count[1]++;
    }
#endif
#endif /*  _INTERRUPT_PROCESS */
    }
}

/*
 * This is the initialization function called from netosStartup() in BSP 
 * when LOCAL_SPI_PORT_SLAVE is configured to BSP_SPI_SLAVE.
 */		
NaStatus initializeSpiSlave(void)
{
    NaStatus rc;
    
#if (LOCAL_SPI_PORT_SLAVE == BSP_SPI_SLAVE)

    if (gSpiSlave != NULL)
    {   /* already initialized */
        rc = NASTATUS_SUCCESS;
        goto _ret;
    }        

    rc = SPI_SLAVE_CREATE_LOCK;
    if (rc != TX_SUCCESS)
    {
#ifdef _DEBUG_PRINTF
        printf("initializeSpiSlave: create_lock fails %d\n", rc);
#endif
        rc = NASTATUS_RESOURCE_ERROR;
        goto _ret;
    }


    SPI_SLAVE_LOCK;

    /* allocate memory for SPI Slave */
    gSpiSlave = (NaSpiSlave_t *)malloc(sizeof(NaSpiSlave_t) * NASPI_MAX_SLAVE_PORT);
    if (gSpiSlave == NULL)
    {
#ifdef _DEBUG_PRINTF
        printf("initializeSpiSlave: malloc fails SPI Slave\n");
#endif
        rc = NASTATUS_MALLOC_ERROR;
        SPI_SLAVE_UNLOCK;
        goto _ret;
    }
    memset(gSpiSlave, 0, sizeof(NaSpiSlave_t));

    /* create number of dma descriptors for TX and RX */
    gSpiSlave->rx_buffer_descriptors = NA_UNCACHED(spi_slave_rx_buffer_descriptors);
    gSpiSlave->tx_buffer_descriptors = NA_UNCACHED(spi_slave_tx_buffer_descriptors);

    gSpiSlave->process_tx_descriptor_index = 0;
    gSpiSlave->process_rx_descriptor_index = 0;
    gSpiSlave->setup_tx_descriptor_index = 0;
    gSpiSlave->setup_rx_descriptor_index = 0;
    gSpiSlave->tx_descriptor_count = 0;
    gSpiSlave->rx_descriptor_count = 0;
    gSpiSlave->dma_rx_start = 0;
    gSpiSlave->dma_tx_start = 0;
    gSpiSlave->max_descriptor_count = SPI_SLAVE_DMA_DESCRIPTORS_COUNT;
    gSpiSlave->user_buffer_count =  SPI_SLAVE_DMA_DESCRIPTORS_COUNT;
    gSpiSlave->user_buffer_list = spi_buffer_list;
    memset(spi_buffer_list, 0, sizeof(spi_buffer_list));

#ifdef _INTERRUPT_PROCESS
    gSpiSlave->user_inactive_buffer_list = spi_inactive_buffer_list;
    memset(spi_inactive_buffer_list, 0, sizeof(spi_inactive_buffer_list));
    gSpiSlave->user_inactive_buffer_count = 0;
    gSpiSlave->user_inactive_buffer_index = 0;
#endif

#ifndef _INTERRUPT_PROCESS
    gSpiSlave->queue_size = sizeof(unsigned long) * NASPI_MAX_SLAVE_DMA_DESCRIPTORS * (2+2);

    gSpiSlave->queue_start = (unsigned long *)malloc(gSpiSlave->queue_size);
    if (gSpiSlave->queue_start == NULL)
    {
#ifdef _DEBUG_PRINTF
        printf("initializeSpiSlave: malloc fails on queue\n");
#endif
        rc = NASTATUS_MALLOC_ERROR;
        SPI_SLAVE_UNLOCK;
        goto _ret;
        
    }

    rc = tx_queue_create(&gSpiSlave->queue, "SPI Slave Active Queue", TX_2_ULONG, (void *)gSpiSlave->queue_start, gSpiSlave->queue_size);
    if (rc != TX_SUCCESS)
    {
#ifdef _DEBUG_PRINTF
        printf("initializeSpiSlave: tx_event_flags_create fails %d\n", rc);
#endif
        rc = NASTATUS_RESOURCE_ERROR;
        SPI_SLAVE_UNLOCK;
        goto _ret;
    }

    gSpiSlave->done_queue_start = (unsigned long *)malloc(gSpiSlave->queue_size);
    if (gSpiSlave->done_queue_start == NULL)
    {
#ifdef _DEBUG_PRINTF
        printf("initializeSpiSlave: malloc fails on queue\n");
#endif
        rc = NASTATUS_MALLOC_ERROR;
        SPI_SLAVE_UNLOCK;
        goto _ret;
        
    }

    rc = tx_queue_create(&gSpiSlave->done_queue, "SPI Slave Done Queue", TX_2_ULONG, (void *)gSpiSlave->done_queue_start, gSpiSlave->queue_size);
    if (rc != TX_SUCCESS)
    {
#ifdef _DEBUG_PRINTF
        printf("initializeSpiSlave: tx_event_flags_create fails %d\n", rc);
#endif
        rc = NASTATUS_RESOURCE_ERROR;
        SPI_SLAVE_UNLOCK;
        goto _ret;
    }

    /* create thread for spi slave to process isr event */
    gSpiSlave->thread_stack = (char *)malloc(SPI_THREAD_STACK_SIZE);
    if (gSpiSlave->thread_stack == NULL)
    {
#ifdef _DEBUG_PRINTF
        printf("initializeSpiSlave: malloc fails SPI thread stack\n");
#endif
        rc = NASTATUS_MALLOC_ERROR;
        SPI_SLAVE_UNLOCK;
        goto _ret;
    }

    rc = tx_thread_create(&gSpiSlave->threadx,      /* control block for root thread */
                              "SPI Slave Thread",    /* thread name */
                              spi_slave_handle,     /* entry function */
                              0,        /* parameter */
                              gSpiSlave->thread_stack,    /* start of stack */
                              SPI_THREAD_STACK_SIZE,       /* size of stack */
                              SPI_THREAD_SLAVE_PRIORITY, /* priority */
                              SPI_THREAD_SLAVE_PRIORITY, /* preemption threshold */
                              1,        /* time slice threshold */
                              TX_AUTO_START);   /* start immediately */

    if (rc != TX_SUCCESS)    /* if couldn't create thread */
    {
#ifdef _DEBUG_PRINTF
        printf("initializeSpiSlave: tx_thread_create fails %d\n", rc);
#endif
        rc = NASTATUS_RESOURCE_ERROR;
        SPI_SLAVE_UNLOCK;
        goto _ret;
    
    }
#endif

    narm_write_reg(NA_SCM_CCR_REG, NA_SCM_CCR_ADDRESS, spi, SCM_CCR_ENABLE_CLOCK);
    spi_reset();

    narm_write_reg (NARM_SPI_CONFIG,  NARM_SPI_CONFIG_REG, slave, SPI_ENABLED); 
    narm_write_reg (NARM_SPI_CONFIG,  NARM_SPI_CONFIG_REG, master, SPI_DISABLED); 

    rc = NASTATUS_SUCCESS;

    SPI_SLAVE_UNLOCK;

_ret:

    if (rc != NASTATUS_SUCCESS)
    {
        
        if (gSpiSlave != NULL) 
        {
#ifndef _INTERRUPT_PROCESS
            if (gSpiSlave->queue_start != NULL)
            {
                tx_queue_delete(&gSpiSlave->queue);
                free(gSpiSlave->queue_start);
            }
            if (gSpiSlave->done_queue_start != NULL)
            {
                tx_queue_delete(&gSpiSlave->done_queue);
                free(gSpiSlave->done_queue_start);
            }
            if (gSpiSlave->thread_stack != NULL) free(gSpiSlave->thread_stack);
#endif
            free(gSpiSlave);
            gSpiSlave = NULL;
        }

    }
    return rc;
#else 
    /* (LOCAL_SPI_PORT_SLAVE != BSP_SPI_SLAVE) */
    gSpiSlave = NULL;

    rc = NASTATUS_SUCCESS;

    return rc;
#endif /* (LOCAL_SPI_PORT_SLAVE == BSP_SPI_SLAVE) */
}

/*ad*
 * Initializes SPI slave.
 * It intializes the SPI Slave for mode and bit order and
 *
 * @category BSP:Device_Drivers:SPI:SPISlaveAPI
 * @since 6.2
 * @external
 *
 * @param port				Port for this device.
 * @param bufferRingSize	Size of the transmitter/receiver buffer ring.
 * @param mode				SPI mode.
 * @param bitOrder		    Set to either NA_SPI_MSB or NA_SPI_LSB
 *
 * @return NASTATUS_SPI_SLAVE_INVALID_SETTINGS	Invalid parameters.
 * @return NASTATUS_NOT_INITIALIZED		        The initialization failed.
 * @return NASTATUS_SPI_SLAVE_ACTIVE		The slave driver already has been initialized for this port.
 * @return NASTATUS_SUCCESS		                The SPI slave driver was successfully initialized.
 * 
 *
 * @see @link NASpiMode
 * @see @link NASpiBitOrder
 * @see @link NaStatusValues
*ad*/
NaStatus NASpiSlaveInitialize(int port, int bufferRingSize, NASpiMode mode, NASpiBitOrder bitOrder)
{

    physical_address_t physTx, physRx;
    NaSpiSlaveDevice_t   * devicePtr;

    NaStatus rc = NASTATUS_SPI_SLAVE_INVALID_SETTINGS;

    SPI_SLAVE_LOCK;

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

    if ((bufferRingSize <= 0) || (bufferRingSize > NASPI_MAX_SLAVE_RING_SIZE))
        goto _ret;

	if (gSpiSlave == NULL)
	{
        rc = initializeSpiSlave();
        if (rc != NASTATUS_SUCCESS)
        {
//		rc = NASTATUS_NOT_INITIALIZED;
		goto _ret;
        }
	}

    /* make sure the device is not actively running (DMA is already setup) */
	if (gSpiSlave->device[port] != NULL && 
	   (gSpiSlave->device[port]->tx_descriptor_count > 0 || gSpiSlave->device[port]->rx_descriptor_count > 0))
	{
		rc = NASTATUS_SPI_SLAVE_ACTIVE;
		goto _ret;
	}

    /* if it's already initialized, just reset set the value settings */
	if (gSpiSlave->device[port] == NULL)
    {
        /* allocate a device for this given port */
        devicePtr = (NaSpiSlaveDevice_t *)malloc(sizeof(NaSpiSlaveDevice_t));
        if (devicePtr == NULL)
        {
            rc = NASTATUS_MALLOC_ERROR;
            goto _ret;
        }

        gSpiSlave->device[port] = devicePtr;
    }
    else
    {
        devicePtr = gSpiSlave->device[port];
    }

	devicePtr->spiMode = mode;
	devicePtr->bitOrder = bitOrder;
    devicePtr->max_descriptor_count = bufferRingSize;
    devicePtr->rx_descriptor_count = 0;
    devicePtr->tx_descriptor_count = 0;
    

    dma_hardware_setup();

    naIsrInstall (SPI_INTERRUPT, (NA_ISR_HANDLER)spi_slave_isr, (void *)SPI_INTERRUPT);

    /*
     * setup DMA buffer descriptor pointer
     */
    if (!NAVaToPhys ((const void *) gSpiSlave->tx_buffer_descriptors, &physTx))
        netosFatalError ("spi tx DMA Cache error", 1, 1);

//    narm_write_reg (NARM_SPI_MOD, NARM_SPI_DMA_TX_DESC_REG, all, physTx); 
    *NARM_SPI_DMA_TX_DESC_REG = physTx; 

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

//    narm_write_reg (NARM_SPI_MOD, NARM_SPI_DMA_RX_DESC_REG, all, physRx); 
    *NARM_SPI_DMA_RX_DESC_REG = physRx; 

    spi_hardware_setup(mode, bitOrder);
	rc = NASTATUS_SUCCESS;

_ret:
    SPI_SLAVE_UNLOCK;
    return rc;
}

/*ad*
 * Sets up transmit and receive buffers.
 *
 * Transmit buffers are transmitted when the master device selects the port and supplies a clock. Transmit buffers
 * are returned to the application using the callback function as they are emptied.
 *
 * Receive buffers are filled when the master device selects the port and supplies a clock. Receive buffers
 * are returned to the application using the callback function as they are filled.
 *
 * @category BSP:Device_Drivers:SPI:SPISlaveAPI
 * @since 7.3
 * @external
 *
 * @param port 			The SPI port number.
 *
 * @param buffer		Pointer to a NASpiSlaveBuffertype structure. 
 *
 * @return NASTATUS_SPI_SLAVE_INVALID_SETTINGS		Invalid arguments.
 * @return NASTATUS_NOT_INITIALIZED		The port has not been initialized.
 * @return NASTATUS_SPI_SLAVE_RING_FULL			The buffer queue is full.
 * @return NASTATUS_SUCCESS				The buffer has been enqueued.
 * 
 *
 * @see @link NASpiSlaveBufferType
 * @see @link NaStatusValues
*ad*/
NaStatus NASpiSlaveEnqueueBuffer(int port, NASpiSlaveBufferType * user_buffer)
{
#ifndef _INTERRUPT_PROCESS
    unsigned long       queue_msg[2];
#endif
    NaSpiSlaveDevice_t  * devicePtr;
    NaStatus            rc = NASTATUS_SPI_SLAVE_INVALID_SETTINGS;
 
    TX_INTERRUPT_SAVE_AREA;

    SPI_SLAVE_LOCK;

    if ((port < 0) || (port >= NASPI_MAX_SLAVE_PORT))
	{
        goto _ret;
	}
    if (user_buffer == NULL || user_buffer->bufferLength > NASPI_MAX_SLAVE_DATA_SIZE)
    {
        goto _ret;
    }
    if (user_buffer->readBuffer == NULL && user_buffer->writeBuffer == NULL)
    {
        /* it's ok for read buffer or write buffer be NULL (but not both), 
         * user may examine the read buffer before setup the write buffer.
         */
        goto _ret;
    }

	if (user_buffer->readBuffer != NULL && ((int)user_buffer->readBuffer % SPI_RECV_BUFFER_ALIGN) != 0)
	{
        goto _ret;
	}


	if (gSpiSlave == NULL)
	{
		rc = NASTATUS_NOT_INITIALIZED;
		goto _ret;
	}


    devicePtr = gSpiSlave->device[port];

	if (devicePtr == NULL)
	{
		rc = NASTATUS_NOT_INITIALIZED;
		goto _ret;
	}

    if (user_buffer->writeBuffer != NULL && devicePtr->tx_descriptor_count == devicePtr->max_descriptor_count)
    {
        rc = NASTATUS_SPI_SLAVE_RING_FULL;
        goto _ret;
    }

    if (user_buffer->readBuffer != NULL && devicePtr->rx_descriptor_count == devicePtr->max_descriptor_count)
    {
        rc = NASTATUS_SPI_SLAVE_RING_FULL;
        goto _ret;
    }

    user_buffer->status = 0;
    user_buffer->byteReceived = 0;
    user_buffer->byteSent = 0;

	TX_DISABLE;

    if (gSpiSlave->tx_descriptor_count != 0 || gSpiSlave->rx_descriptor_count != 0)
    {
#ifndef _INTERRUPT_PROCESS
        queue_msg[0] = (unsigned long)devicePtr;
        queue_msg[1] = (unsigned long)user_buffer;

        rc = tx_queue_send(&gSpiSlave->queue, (void *)&queue_msg, TX_NO_WAIT);
        if (rc != TX_SUCCESS)
#else
        rc = queue_inactive_buffer(devicePtr, user_buffer);
        if (rc != 0)
#endif
        {
            rc = NASTATUS_SPI_SLAVE_INVALID_SETTINGS;
            if (rc == TX_QUEUE_FULL)
            {
                rc = NASTATUS_SPI_SLAVE_RING_FULL;
            }
            goto _ret;
        }
    }
    else
    {
        rc = add_spi_buffer(devicePtr, user_buffer);
        if (rc != 0)
        {
            rc = NASTATUS_SPI_SLAVE_INVALID_SETTINGS;
            if (rc == TX_QUEUE_FULL)
            {
                rc = NASTATUS_SPI_SLAVE_RING_FULL;
            }
            goto _ret;
        }
        spi_reset();

//        dma_hardware_setup();
        
        spi_hardware_setup(devicePtr->spiMode, devicePtr->bitOrder);

        /* setup DMA */
        dma_descriptor_setup(devicePtr, user_buffer->writeBuffer, user_buffer->readBuffer, user_buffer->bufferLength);

        if (user_buffer->writeBuffer != NULL)
        {   /* start tx DMA if it has not been started yet */
            gSpiSlave->dma_tx_start = 1;
            enable_tx_spi();
        }

        if (user_buffer->readBuffer != NULL)
        {   /* start rx DMA if it's not been started yet */
            gSpiSlave->dma_rx_start = 1;
            enable_rx_spi();
        }
    }

    if (user_buffer->writeBuffer != NULL)
    {   /* start tx DMA if it has not been started yet */
        gSpiSlave->tx_descriptor_count++;
    }

    if (user_buffer->readBuffer != NULL)
    {   /* start rx DMA if it's not been started yet */
        gSpiSlave->rx_descriptor_count++;
    }

    TX_RESTORE;

	rc = NASTATUS_SUCCESS;

_ret:

    SPI_SLAVE_UNLOCK;

	return rc;
}


