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


/* Customer Avery uses TX and CLK pins to send data to only one device though SPI port.
   uncomment this define will speed up their multiple SPI send operations. */
//#define AVERY_SPI_USAGE

/*ad*
 *
 * The simple SPI master driver API consists of:
 * <list type="unordered">
 * 		<item>A set of registration/deregistration functions to dynamically
 *			  add or delete a SPI device</item>
 *		<item>A single function that simultaneously reads and writes to a
 *			  single SPI device</item>
 * </list>
 *
 * The simple SPI master driver API functions include:
 *
 * <list type="unordered">
 *     <item>@link NASpiRegisterDevice </item>
 *     <item>@link NASpiDeregisterDevice </item>
 *     <item>@link NASpiReadWrite </item>
 * </list>
 *
 * @external
 * @overview SPI Master Driver
 * @include 	"spi_master_api.h"
 * @library 	bsp
 * @platform 	all
 * @category BSP:Device_Drivers:SPI:SPIMasterAPI
 * @since 6.2
*ad*/

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

/*
 * Serial port registers' starting address
 */
static SPIREG_TYPE *spiRegs[NASPI_MAX_MASTER_PORT] = {
    (SPIREG_TYPE *) NARM_SC1CRA_ADDR,
    (SPIREG_TYPE *) NARM_SC2CRA_ADDR,
#ifdef BSP_ARM9
    (SPIREG_TYPE *) NARM_SC3CRA_ADDR,
    (SPIREG_TYPE *) NARM_SC4CRA_ADDR
#endif
};



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

#ifdef BSP_ARM9
    (DMAREG_TYPE *) 0x90000040,         /* DMA Channel 3 */
    (DMAREG_TYPE *) 0x90000000,         /* DMA Channel 1 */
    (DMAREG_TYPE *) 0x90000080,         /* DMA Channel 5 */
    (DMAREG_TYPE *) 0x900000c0,         /* DMA Channel 7 */
#else
    (DMAREG_TYPE *) NARM_DBDP7_ADDR,
    (DMAREG_TYPE *) NARM_DBDP9_ADDR,
#endif
};

#ifndef BSP_ARM9
#define NA_GPIO_FUNC0_STATE		0       /*special function output */
#define NA_GPIO_FUNC1_STATE		1       /*special function input */
#define NA_GPIO_INPUT_STATE		2       /*GPIO input */
#define NA_GPIO_OUTPUT_STATE	3       /*GPIO output */
static void NAconfigureGPIOpin (int port, int pinNum, int state);
static void NAsetGPIOpin (int port, int pinNum, int state);
#endif

TX_SEMAPHORE spi_port_sem[NASPI_MAX_MASTER_PORT];

/*
 * serial port semaphore names
 */
static char port_sem_name[NASPI_MAX_MASTER_PORT][10] = {
    "spi0",
    "spi1",
#ifdef BSP_ARM9
    "spi2",
    "spi3"
#endif
};

/*
 * Cache related info for ns9750 and ns9360
 * rx_assoc holds the virtual addresses of the rx descriptor data buffers.
 * tx_assoc holds the virtual addresses of the tx descriptor data buffers.
 */
#ifdef BSP_ARM9
static unsigned long rx_assoc[NASPI_MAX_MASTER_PORT];
static unsigned long tx_assoc[NASPI_MAX_MASTER_PORT];
static NA_DMA_DESCRIPTOR_TYPE txc_buffer_descriptors[NASPI_MAX_MASTER_PORT][NASPI_MAX_DESCRIPTORS];
static NA_DMA_DESCRIPTOR_TYPE rxc_buffer_descriptors[NASPI_MAX_MASTER_PORT][NASPI_MAX_DESCRIPTORS];
#else
static fb_buffer_desc_t txc_buffer_descriptors[NASPI_MAX_MASTER_PORT][NASPI_MAX_DESCRIPTORS];   /* tx DMA buffer descriptors */
static fb_buffer_desc_t rxc_buffer_descriptors[NASPI_MAX_MASTER_PORT][NASPI_MAX_DESCRIPTORS];   /* rx DMA buffer descriptors */
#endif

/* 
 * device table for the whole system 
 */
SPI_DEVICETABLE_TYPE spiDeviceTable[NASPI_MAX_DEVICE];
SPIPORT_TYPE spiPorts[NASPI_MAX_MASTER_PORT];

/*
 * Local functions
 */
static void spi_tx_dma_int (int port);
static void spi_rx_dma_int (int port);

static void setupSpiPort (NASpiDeviceConfigType * spiDevice);
static void setupDma (int port, char *const writeBuffer, char *readBuffer, int bufferLength);
static void enablePort (int port);
#ifndef AVERY_SPI_USAGE
static void disablePort (int port);
static void selectCs (NASpiDeviceConfigType * spiDevice);
static void deselectCs (NASpiDeviceConfigType * spiDevice);
#endif /* AVERY_SPI_USAGE */
static void initSpiPort (int port);
static void configBaudDivisor (int port, int baudrate);

/*
 * Cache related for ns9750 and ns9360. these are here so that the spi boot loader will not try to link to 
 * MMU routines.
 */
static void *DoesNothing1 (const void *);
static void DoesNothing2 (na_dma_operation_t, const void *, const void *, unsigned long);
static int DoesNothing3 (const void *virtual_address, physical_address_t * physical_address);
void *(*spiNA_UNCACHED) (const void *) = DoesNothing1;
void (*spiNABeforeDMA) (na_dma_operation_t, const void *, const void *, unsigned long) = DoesNothing2;
int (*spiNAVaToPhys) (const void *virtual_address, physical_address_t * physical_address) = DoesNothing3;
void (*spiNAAfterDMA) (na_dma_operation_t, const void *, const void *, unsigned long) = DoesNothing2;
static void *DoesNothing1 (const void *p1)
{
    return (void *) p1;
}
static void DoesNothing2 (na_dma_operation_t p1, const void *p2, const void *p3, unsigned long p4)
{
    NA_UNUSED_PARAM(p1);
    NA_UNUSED_PARAM(p2);
    NA_UNUSED_PARAM(p3);
    NA_UNUSED_PARAM(p4);
    return;
}
static int DoesNothing3 (const void *virtual_address, physical_address_t * physical_address)
{
    *physical_address = (physical_address_t) virtual_address;
    return TRUE;
}

/*
 * This is the initialization function called from netosStartup() in BSP
 * according to the serial port settings in bsp.h in each platform.
 */
NaStatus initializeSpiMaster (void)
{

#if (BSP_SERIAL_PORT_1 == BSP_SERIAL_SPI_DRIVER)
    initSpiPort (0);
#endif

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

#ifdef BSP_ARM9
#if (BSP_SERIAL_PORT_3 == BSP_SERIAL_SPI_DRIVER)
    initSpiPort (2);
#endif

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

    memset (spiDeviceTable, 0, sizeof spiDeviceTable);
    return NASTATUS_SUCCESS;
}

/*
 * Initialize a spi port, called by initializeSpiMaster() during BSP start up
 */
static void initSpiPort (int port)
{
    int rx_int_id = 0, tx_int_id = 0, enable = 0;

#ifndef BSP_ARM9
    int dout, din, clk;
#endif
    NA_UNUSED_PARAM(initSpiPort);

    tx_semaphore_create (&spi_port_sem[port], port_sem_name[port], 1);

#ifdef BSP_ARM9
    switch (port)
    {
    case 0:
        rx_int_id = BBUS_DMA03_INTERRUPT;
        tx_int_id = BBUS_DMA04_INTERRUPT;
       
        enable = 15;
        /* set endian */
        narm_write_reg(NA_BUM_ECR_REG, NA_BUM_ECR_ADDRESS, ser2, 0);
        break;
    case 1:
        rx_int_id = BBUS_DMA01_INTERRUPT;
        tx_int_id = BBUS_DMA02_INTERRUPT;
       
        enable = 7;
        /* set endian */
        narm_write_reg(NA_BUM_ECR_REG, NA_BUM_ECR_ADDRESS, ser1, 0);
        break;
    case 2:
        rx_int_id = BBUS_DMA05_INTERRUPT;
        tx_int_id = BBUS_DMA06_INTERRUPT;
        
        enable = 23;
        /* set endian */
        narm_write_reg(NA_BUM_ECR_REG, NA_BUM_ECR_ADDRESS, ser3, 0);
        break;
    case 3:
        rx_int_id = BBUS_DMA07_INTERRUPT;
        tx_int_id = BBUS_DMA08_INTERRUPT;
       
        enable = 27;
        /* set endian */
        narm_write_reg(NA_BUM_ECR_REG, NA_BUM_ECR_ADDRESS, ser4, 0);
        break;
    }
    spiPorts[port].defaultCsPin = enable;


#else
    switch (port)
    {
    case 0:
        rx_int_id = DMA7_INT;
        tx_int_id = DMA8_INT;
        break;
    case 1:
        rx_int_id = DMA9_INT;
        tx_int_id = DMA10_INT;
        break;
    }

    dout = 7;
    din = 3;
    clk = 4;
    enable = 0;
    NAconfigureGPIOpin (port, dout, NA_GPIO_FUNC0_STATE);       /*dout */
    NAconfigureGPIOpin (port, din, NA_GPIO_FUNC1_STATE);        /*din */
    NAconfigureGPIOpin (port, clk, NA_GPIO_FUNC0_STATE);        /*clk */

    if (narm_read_reg (NARM_GSSR_REG, NARM_GSSR_ADDR, rev) != NETA50_4)
    {
        NAconfigureGPIOpin (port, enable, NA_GPIO_INPUT_STATE); /*enable bit as GPIO input */
        spiPorts[port].defaultCsPin = enable;
        spiPorts[port].defaultCsPort = port;
    }
    else
    {
        NAconfigureGPIOpin (2 /*always port C */ , (port ? 5 : 7), NA_GPIO_INPUT_STATE);        /*enable bit as GPIO input */
        spiPorts[port].defaultCsPin = port ? 5 : 7;
        spiPorts[port].defaultCsPort = 2;       /*always port C */
    }

#endif

    spiPorts[port].tx_buffer_descriptors = spiNA_UNCACHED (&txc_buffer_descriptors[port][0]);
    spiPorts[port].rx_buffer_descriptors = spiNA_UNCACHED (&rxc_buffer_descriptors[port][0]);

    /*NAInvalidateBuffer(spiPorts[port].tx_buffer_descriptors, sizeof txc_buffer_descriptors);
       NAInvalidateBuffer(spiPorts[port].rx_buffer_descriptors, sizeof rxc_buffer_descriptors); */

    spiMasterInstallIsr (rx_int_id, (void *) spi_rx_dma_int, (void *) port);
    spiMasterInstallIsr (tx_int_id, (void *) spi_tx_dma_int, (void *) port);

    spiPorts[port].opened = 1;
    spiPorts[port].spiRegs = spiRegs[port];
    spiPorts[port].dmaRegs = dmaRegs[port];
    spiPorts[port].rx_int_flag = 0;
    spiPorts[port].tx_int_flag = 0;
    spiPorts[port].rx_int_id = rx_int_id;
    spiPorts[port].tx_int_id = tx_int_id;

    spiMasterDisableIsr (spiPorts[port].rx_int_id);
    spiMasterDisableIsr (spiPorts[port].tx_int_id);

#ifdef BSP_ARM9
    /* reset SPI */
    narm_write_reg (NA_DMA_DCR, &spiPorts[port].dmaRegs->dmarcr, mustSetToZero, 1);      /* Rx reset */
    narm_write_reg (NA_DMA_DCR, &spiPorts[port].dmaRegs->dmatcr, mustSetToZero, 1);      /* Tx reset */

//    NAWait(1);

    narm_write_reg (NA_DMA_DCR, &spiPorts[port].dmaRegs->dmarcr, mustSetToZero, 0);      
    narm_write_reg (NA_DMA_DCR, &spiPorts[port].dmaRegs->dmatcr, mustSetToZero, 0);      
#endif
    /* initialize SPI Master port with out mode, bitorder and baud */
    /*spiPorts[port].spiRegs->ctrA = 0;*/
    narm_write_reg (NARM_SCRA_REG, &spiPorts[port].spiRegs->ctrA, ce, 0);       /*disable the port */
    narm_write_reg (NARM_SCRA_REG, &spiPorts[port].spiRegs->ctrA, wls, 3);      /*word length of 8 data bits */
    narm_write_reg (NARM_SCRA_REG, &spiPorts[port].spiRegs->ctrA, rl, 0);       /*disable remote loop back */
    narm_write_reg (NARM_SCRA_REG, &spiPorts[port].spiRegs->ctrA, ll, 0);       /*disable remote loop back */
    narm_write_reg (NARM_SCRA_REG, &spiPorts[port].spiRegs->ctrA, erxbc, 1);    /*enable the interrupt on receive buffer closed */
    narm_write_reg (NARM_SCRA_REG, &spiPorts[port].spiRegs->ctrA, erxdma, 1);   /*enable receive DMA */
    narm_write_reg (NARM_SCRA_REG, &spiPorts[port].spiRegs->ctrA, etxbc, 1);    /*enable the interrupt on transmit buffer closed */
    narm_write_reg (NARM_SCRA_REG, &spiPorts[port].spiRegs->ctrA, etxdma, 1);   /*enable transmit DMA */

    /*
     * setup serial control register B
     */
    /* spiPorts[port].spiRegs->ctrB = 0; */
    narm_write_reg (NARM_SCRB_REG, &spiPorts[port].spiRegs->ctrB, mode, 2);     /*SPI master mode */
    narm_write_reg (NARM_SCRB_REG, &spiPorts[port].spiRegs->ctrB, bitordr, 0);        /*bit order */

#if 0
    /*
     * setup serial bit-rate register
     */
   /* spiPorts[port].spiRegs->brg = 0;*/
    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, ebit, 1);      /*bit-rate generator enable */
    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, tmode, 1);     /*timing mode must be 1 */
    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, rxsrc, 0);     /*internal timing source */
    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, txsrc, 0);     /*internal timing source */
    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, rxext, 0);     /*disable driver receive clock external */
    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, txext, 1);     /*enable transmitter clock to be driven on GPIO pin */

#ifdef BSP_ARM9
        narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, txcinv, 0);        /*transmit on clock falling edge */
        narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, rxcinv, 0);

        narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, spcpol, 0);        /*clock idle high */
#else
    if (narm_read_reg (NARM_GSSR_REG, NARM_GSSR_ADDR, rev) != NETA50_4)
    {
        /* these fields must be set to 0 for NS7520 */
        narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, txcinv, 0);
        narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, rxcinv, 0);
    }
    else
    {
            narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, txcinv, 0);    /*transmit on clock falling edge */
            narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, rxcinv, 0);
            narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, clkinv, 0);    /*clock idle low */
    }

#endif


    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, tcdr, 0);      /* 1x transmit clock */
    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, rcdr, 0);      /* 1x receive clock */
    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, tics, 0);      /* bit-rate generator output as transmit clock */
    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, rics, 0);      /* bit-rate generator output as receive clock */

    /*
     * Disable dma
     */
#ifdef BSP_ARM9
    narm_write_reg (NA_DMA_DCR, &spiPorts[port].dmaRegs->dmarcr, ce, 0);
    narm_write_reg (NA_DMA_DCR, &spiPorts[port].dmaRegs->dmatcr, ce, 0);
#else
    narm_write_reg (NARM_DCR_REG, &spiPorts[port].dmaRegs->dmarcr, ce, 0);
    narm_write_reg (NARM_DCR_REG, &spiPorts[port].dmaRegs->dmatcr, ce, 0);
#endif

#endif

}

/*ad*
 * Registers a device to the SPI device table.
 * 
 *
 * @category BSP:Device_Drivers:SPI:SPIMasterAPI
 * @since 6.2
 * @external
 *
 * @param spiDevice 			Device settings
 *
 * @return NASTATUS_SPI_MASTER_DUPLICATE_DEVICE		The device can't be registered with this name because
 *													a device with the same name already exits.
 * @return NASTATUS_RESOURCE_ERROR			The device table is full.
 * @return NASTATUS_SPI_MASTER_INVALID_SETTINGS		The device settings are invalid.
 * @return NASTATUS_SUCCESS				The operation was successful.
 * 
 *
 * @see @link NASpiDeviceConfigType
 * @see @link NaStatusValues
*ad*/
NaStatus NASpiRegisterDevice(NASpiDeviceConfigType * spiDevice)
{

    int i;
    NaStatus ret = NASTATUS_SPI_MASTER_INVALID_SETTINGS;
    int maxSpeed;

    INTERRUPT_SAVE_AREA;

    if (spiDevice == NULL)
        goto Exit;

    if (spiDevice->port >= NASPI_MAX_MASTER_PORT)
    {
        goto Exit;
    }
    if (!spiPorts[spiDevice->port].opened)
    {
        ret = initializeSpiMaster();
        if (ret != NASTATUS_SUCCESS)
        {
        goto Exit;
        }
    }

    if (spiDevice->name == NULL || strlen (spiDevice->name)==0 || strlen (spiDevice->name) > NASPI_MAX_NAME_LENGTH)
        goto Exit;

#ifdef BSP_ARM9
#ifdef NS9750
    maxSpeed = NAgetBbusClkFreq () / 6;
#else
    maxSpeed = NAgetBbusClkFreq () / 4;
#endif
#else
    maxSpeed = NAgetCpuClkFreq () / 8;
#endif
    if (spiDevice->baudRate < 0 || spiDevice->baudRate > maxSpeed)
        goto Exit;

    /*
     * does it exist already?
     */
    DISABLE_INTS;
    for (i = 0; i < NASPI_MAX_DEVICE; i++)
    {
        if (spiDeviceTable[i].registered && !strcmp (spiDevice->name, spiDeviceTable[i].device.name))
        {
            ret = NASTATUS_SPI_MASTER_DUPLICATE_DEVICE;
            RESTORE_INTS;
            goto Exit;
        }
    }
    /*
     * Is there empty spot in the spiDeviceTable?
     */
    for (i = 0; i < NASPI_MAX_DEVICE; i++)
    {
        if (!spiDeviceTable[i].registered)
            break;
    }

    if (i == NASPI_MAX_DEVICE)
    {
        ret = NASTATUS_RESOURCE_ERROR;
        RESTORE_INTS;
        goto Exit;
    }
    else
    {
        spiDeviceTable[i].registered = 1;
        spiDeviceTable[i].inuse = 0;
        memcpy (&spiDeviceTable[i].device, spiDevice, sizeof (spiDeviceTable[i].device));
        RESTORE_INTS;
        ret = NASTATUS_SUCCESS;
    }

	/* 
     * get semaphore 
     */
    tx_semaphore_get (&spi_port_sem[spiDevice->port], TX_WAIT_FOREVER);

	
	setupSpiPort (spiDevice);

	/* 
     * release semaphore 
     */
    tx_semaphore_put (&spi_port_sem[spiDevice->port]);

  Exit:
    return ret;
}

/*ad*
 * Deregisters a device from the SPI device table.
 * 
 *
 * @category BSP:Device_Drivers:SPI:SPIMasterAPI
 * @since 6.2
 * @external
 *
 * @param name 			The name of the device
 *
 * @return NASTATUS_SPI_MASTER_DEVICE_NOT_FOUND		The device is not registed and can't be used.
 * @return NASTATUS_SPI_MASTER_DEVICE_ACTIVE				The device is in use and can't be deregistered.
 * @return NASTATUS_SUCCESS				The operation was successful.
 * 
 *
 * @see @link NASpiDeviceConfigType
 * @see @link NaStatusValues
*ad*/
NaStatus NASpiDeregisterDevice(char * name)
{
    INTERRUPT_SAVE_AREA;
    int i, ret = NASTATUS_SPI_MASTER_DEVICE_NOT_FOUND;

    if (name == NULL || strlen (name) > NASPI_MAX_NAME_LENGTH)
        goto Exit;

    /*
     * does it exist?
     */
    DISABLE_INTS;
    for (i = 0; i < NASPI_MAX_DEVICE; i++)
    {
        if (spiDeviceTable[i].registered && !strcmp (name, spiDeviceTable[i].device.name))
        {
            if (!spiDeviceTable[i].inuse)
            {
                spiDeviceTable[i].registered = 0;
                spiDeviceTable[i].inuse = 0;
                memset (&spiDeviceTable[i].device, 0, sizeof (NASpiDeviceConfigType));
                ret = NASTATUS_SUCCESS;
                RESTORE_INTS;
                goto Exit;
            }
            else
                ret = NASTATUS_SPI_MASTER_DEVICE_ACTIVE;
        }
    }

    RESTORE_INTS;
  Exit:
    return ret;
}

/*ad*
 * This routine is the only function for SPI master.  It is protected with a semaphore to make it thread safe.
 * 
 * This function does the following:
 *
 *	<list type = "ordered">
 *      <item>The parameter readBuffer is checked to make sure it is on a 4-byte boundary.  
 *            The function terminates in error if it isn't.</item>
 *      <item>The parameter name is used as an index into NASpiDeviceTable.  
 *            The SPI port is opened and configured according to the settings in that table entry.</item>
 *      <item>The buffers are enqueued in the port's DMA buffer descriptor rings.</item>
 *      <item>The device's chip select is asserted.</item>
 *      <item>The DMA transfer is started.</item>
 *      <item>The function blocks until the DMA transfer as completed.</item>
 *      <item>The chip select is negated.</item>
 *      <item>The function returns, with data received from the device in the buffer pointed to 
 *            by the readBuffer parameter.</item>
 *  </list>
 *
 * The readBuffer must be on 4-byte boundary, and the readBuffer and writeBuffer must be the same length. 
 * This function blocks until it completes the data transfer. The serial Flash driver, NVRAM driver 
 * and SPI boot loader are built on top of this function.
 *
 *
 * @category BSP:Device_Drivers:SPI:SPIMasterAPI
 * @since 6.2
 * @external
 *
 * @param name 			device name
 * @param writeBuffer	data buffer that holds the data to be transmitted
 * @param readBuffer    data buffer to hold the received data
 * @param bufferLength	the length of the transmitted data
 *
 * @return NASTATUS_SPI_MASTER_DEVICE_NOT_FOUND		The device is not registed and can't be used.
 * @return NASTATUS_SPI_MASTER_INVALID_BUFFER   	The read buffer is either not 4-byte aligned or is NULL,
 *													or the write buffer is NULL.
 * @return NASTATUS_SPI_MASTER_DMA_FAIL			    The DMA transmit or receive failed.
 * @return NASTATUS_SUCCESS				            The operation was successful.
 * 
 *
 * @see @link NASpiDeviceConfigType
 * @see @link NaStatusValues
*ad*/
NaStatus NASpiReadWrite(char * name, char * const writeBuffer, char * readBuffer, int bufferLength)
{
    INTERRUPT_SAVE_AREA;
    NaStatus ret = NASTATUS_SPI_MASTER_DEVICE_NOT_FOUND;
    int i;
    NASpiDeviceConfigType *devicePtr;
    SPI_DEVICETABLE_TYPE  *spiPtr;
    int port;

#ifdef BSP_ARM9
    int rx_count, tx_count;
    unsigned long virtualBuffer;
    int timeout = 100000000;    /*100 seconds */
#else
    int timeout = 10000;        /*100 seconds */
#endif 

    if (name == NULL || strlen (name) > NASPI_MAX_NAME_LENGTH)
        goto Exit;

    if (readBuffer == NULL || writeBuffer == NULL || bufferLength > NASPI_MAX_DATA_LENGTH ||
        (int) readBuffer % 4)
    {
        ret = NASTATUS_SPI_MASTER_INVALID_BUFFER;
        goto Exit;
    }

	if (!bufferLength)
	{
        ret = NASTATUS_SUCCESS;
        goto Exit;
    }

	
    /*
     * does it exist?
     */
    DISABLE_INTS;
    for (i = 0; i < NASPI_MAX_DEVICE; i++)
    {
        if (spiDeviceTable[i].registered && !spiDeviceTable[i].inuse && !strcmp (name, spiDeviceTable[i].device.name))
            break;
    }

    if (i == NASPI_MAX_DEVICE)
    {
        RESTORE_INTS;
        goto Exit;
    }

    spiPtr = &spiDeviceTable[i];
    spiPtr->inuse = 1;
    devicePtr = &spiPtr->device;
    port = devicePtr->port;

    RESTORE_INTS;
    
    /* 
     * get semaphore 
     */
    tx_semaphore_get (&spi_port_sem[port], TX_WAIT_FOREVER);

#ifndef AVERY_SPI_USAGE
    deselectCs (devicePtr);
    setupSpiPort (devicePtr);
#endif /* AVERY_SPI_USAGE */
    setupDma (port, writeBuffer, readBuffer, bufferLength);
#ifndef AVERY_SPI_USAGE
    selectCs (devicePtr);
#endif /* AVERY_SPI_USAGE */
    enablePort (port);

    while (timeout && (!spiPorts[port].tx_int_flag || !spiPorts[port].rx_int_flag))
    {
        /* Should this be a tx_thread_sleep(1) or something in order to 
         * allow lower priority threads to run as well? 
         */
#ifdef BSP_ARM9
		if (bufferLength < 8000)
			NAuWait (1);
		else
			tx_thread_sleep (1);
#else
        tx_thread_sleep (1);
#endif

        timeout--;
    }

    if (!timeout)
        ret = NASTATUS_SPI_MASTER_DMA_FAILED;
    else
        ret = NASTATUS_SUCCESS;


#ifdef BSP_ARM9
    rx_count = (bufferLength / NASPI_MAX_DMABUFFER_LENGTH) + ((bufferLength % NASPI_MAX_DMABUFFER_LENGTH) ? 1 : 0);
    tx_count = rx_count;

    while (rx_count > 0 || tx_count > 0)
    {
        for (i = 0; i < NASPI_MAX_DESCRIPTORS; i++)
        {
            if (spiPorts[port].tx_buffer_descriptors[i].src_addr != 0x0 && (spiPorts[port].tx_buffer_descriptors[i].flag & DMA_FULL_MASK) != DMA_FULL_MASK)
            {
                spiPorts[port].tx_buffer_descriptors[i].src_addr = 0x0;
                tx_count--;
            }

            if (spiPorts[port].rx_buffer_descriptors[i].src_addr != 0x0 && (spiPorts[port].rx_buffer_descriptors[i].flag & DMA_FULL_MASK) == DMA_FULL_MASK)
            {
                spiPorts[port].rx_buffer_descriptors[i].src_addr = 0x0;
                rx_count--;
            }
        }
        if (rx_count > 0 || tx_count > 0)
        {
            tx_thread_sleep(5);
        }
    }
#endif
        
    /*
     * Convert the physical address back to virtual address
     */
#ifdef BSP_ARM9
    virtualBuffer = tx_assoc[port];
    NA_ASSERT (virtualBuffer);
//    spiNAAfterDMA (NA_MEMORY_TO_DEVICE_DMA, NULL, (const void *) virtualBuffer, bufferLength);
    spiNAAfterDMA (NA_MEMORY_TO_DEVICE_DMA, (const void *) writeBuffer, NULL, bufferLength);

    virtualBuffer = rx_assoc[port];
    NA_ASSERT (virtualBuffer);
//    spiNAAfterDMA (NA_DEVICE_TO_MEMORY_DMA, NULL, (const void *) virtualBuffer, bufferLength);
    spiNAAfterDMA (NA_DEVICE_TO_MEMORY_DMA, NULL, (const void *) readBuffer, bufferLength);
#endif

#ifndef AVERY_SPI_USAGE
#ifdef BSP_ARM9
    NAuWait (devicePtr->deselectTime);
#else
    NAWait (devicePtr->deselectTime / 10);
#endif

    disablePort (port);
    deselectCs (devicePtr);
#endif /* AVERY_SPI_USAGE */
    /* 
     * release semaphore 
     */
    tx_semaphore_put (&spi_port_sem[port]);

    spiPtr->inuse = 0;

  Exit:

    return ret;
}

/*
 * This routine configures the serial port as SPI master
 */
static void setupSpiPort (NASpiDeviceConfigType * spiDevice)
{
    int port = spiDevice->port;

    /*
     * setup serial control register A
     */
    /*spiPorts[port].spiRegs->ctrA = 0;*/
    narm_write_reg (NARM_SCRA_REG, &spiPorts[port].spiRegs->ctrA, ce, 0);       /*disable the port */
    narm_write_reg (NARM_SCRA_REG, &spiPorts[port].spiRegs->ctrA, wls, 3);      /*word length of 8 data bits */
    narm_write_reg (NARM_SCRA_REG, &spiPorts[port].spiRegs->ctrA, rl, 0);       /*disable remote loop back */
    narm_write_reg (NARM_SCRA_REG, &spiPorts[port].spiRegs->ctrA, ll, 0);       /*disable remote loop back */
    narm_write_reg (NARM_SCRA_REG, &spiPorts[port].spiRegs->ctrA, erxbc, 1);    /*enable the interrupt on receive buffer closed */
    narm_write_reg (NARM_SCRA_REG, &spiPorts[port].spiRegs->ctrA, erxdma, 1);   /*enable receive DMA */
    narm_write_reg (NARM_SCRA_REG, &spiPorts[port].spiRegs->ctrA, etxbc, 1);    /*enable the interrupt on transmit buffer closed */
    narm_write_reg (NARM_SCRA_REG, &spiPorts[port].spiRegs->ctrA, etxdma, 1);   /*enable transmit DMA */

    /*
     * setup serial control register B
     */
    /*spiPorts[port].spiRegs->ctrB = 0;*/
    narm_write_reg (NARM_SCRB_REG, &spiPorts[port].spiRegs->ctrB, mode, 2);     /*SPI master mode */
    narm_write_reg (NARM_SCRB_REG, &spiPorts[port].spiRegs->ctrB, bitordr, spiDevice->bitOrder);        /*bit order */

    /*
     * setup serial bit-rate register
     */
   /* spiPorts[port].spiRegs->brg = 0;*/
    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, ebit, 1);      /*bit-rate generator enable */
    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, tmode, 1);     /*timing mode must be 1 */
    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, rxsrc, 0);     /*internal timing source */
    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, txsrc, 0);     /*internal timing source */
    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, rxext, 0);     /*disable driver receive clock external */
    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, txext, 1);     /*enable transmitter clock to be driven on GPIO pin */

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

    if (spiDevice->spiMode == NA_SPI_MODE1 || spiDevice->spiMode == NA_SPI_MODE3)
    {
        narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, spcpol, 0);        /*clock idle high */
    }
    else
    {
        narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, spcpol, 1);        /*clock idle low */
    }
#else
    if (narm_read_reg (NARM_GSSR_REG, NARM_GSSR_ADDR, rev) != NETA50_4)
    {
        /* these fields must be set to 0 for NS7520 */
        narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, txcinv, 0);
        narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, rxcinv, 0);
    }
    else
    {
        if (spiDevice->spiMode == NA_SPI_MODE0 || spiDevice->spiMode == NA_SPI_MODE3)
        {
            narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, txcinv, 0);    /*transmit on clock falling edge */
            narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, rxcinv, 0);
        }
        else
        {
            narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, txcinv, 1);    /*transmit on clock risinging edge */
            narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, rxcinv, 1);
        }

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

#endif
    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, tcdr, 0);      /* 1x transmit clock */
    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, rcdr, 0);      /* 1x receive clock */
    configBaudDivisor (port, spiDevice->baudRate);      /*setup N value */
    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, tics, 0);      /* bit-rate generator output as transmit clock */
    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, rics, 0);      /* bit-rate generator output as receive clock */
}


/*
 * This routine sets up DMA
 */
static void setupDma (int port, char *const writeBuffer, char *readBuffer, int bufferLength)
{

    int i, byteLeft = bufferLength;
    physical_address_t physTx, physRx, phys;

    memset (spiPorts[port].tx_buffer_descriptors, 0, sizeof (txc_buffer_descriptors[port]));
    memset (spiPorts[port].rx_buffer_descriptors, 0, sizeof (rxc_buffer_descriptors[port]));

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

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

    /*
     * RX
     */

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

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

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

    /*
     * setup buffer descriptor
     */

    for (i = 0; i < NASPI_MAX_DESCRIPTORS; i++)
    {
        /*Setup the tx buffer descriptor, and use the physical address in the DMA buffer descriptor */
        spiPorts[port].tx_buffer_descriptors[i].src_addr = physTx + i * NASPI_MAX_DMABUFFER_LENGTH;
        spiPorts[port].rx_buffer_descriptors[i].src_addr = physRx + i * NASPI_MAX_DMABUFFER_LENGTH;
        spiPorts[port].tx_buffer_descriptors[i].dst_addr = 0;
        spiPorts[port].rx_buffer_descriptors[i].dst_addr = 0;

        if (byteLeft <= NASPI_MAX_DMABUFFER_LENGTH)
        {
            spiPorts[port].tx_buffer_descriptors[i].buf_len = byteLeft;
            spiPorts[port].rx_buffer_descriptors[i].buf_len = byteLeft;
            spiPorts[port].tx_buffer_descriptors[i].flag = 0;
            spiPorts[port].rx_buffer_descriptors[i].flag = 0;
            spiPorts[port].tx_buffer_descriptors[i].flag = DMA_WRAP_MASK | DMA_INTERRUPT_MASK | DMA_LAST_MASK | DMA_FULL_MASK;
            spiPorts[port].rx_buffer_descriptors[i].flag = DMA_WRAP_MASK | DMA_INTERRUPT_MASK | DMA_LAST_MASK;
            break;
        }
        else
        {
            spiPorts[port].tx_buffer_descriptors[i].buf_len = NASPI_MAX_DMABUFFER_LENGTH;
            spiPorts[port].rx_buffer_descriptors[i].buf_len = NASPI_MAX_DMABUFFER_LENGTH;
            spiPorts[port].tx_buffer_descriptors[i].flag = 0;
            spiPorts[port].rx_buffer_descriptors[i].flag = 0;
            spiPorts[port].tx_buffer_descriptors[i].flag = DMA_FULL_MASK;       /*do not enable tx interrupt except on the last one */
            spiPorts[port].rx_buffer_descriptors[i].flag = 0;   /*do not enable rx interrupt except on the last one */
        }

        byteLeft -= NASPI_MAX_DMABUFFER_LENGTH;

    }

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

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

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

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

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

    /* 
     * setup DMA status/interrupt register
     */
    narm_write_reg (NA_DMA_DSIER, &spiPorts[port].dmaRegs->dmarsr, ncie, 1);    /*use NRIP */
    narm_write_reg (NA_DMA_DSIER, &spiPorts[port].dmaRegs->dmatsr, ncie, 1);    /*use NRIP */

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

    narm_write_reg (NA_DMA_DSIER, &spiPorts[port].dmaRegs->dmarsr, nrie, 1);    /*disable nrip interrupt */
    narm_write_reg (NA_DMA_DSIER, &spiPorts[port].dmaRegs->dmatsr, nrie, 1);    /*disable nrip interrupt */

    narm_write_reg (NA_DMA_DSIER, &spiPorts[port].dmaRegs->dmarsr, caie, 1);    /*disable nrip interrupt */
    narm_write_reg (NA_DMA_DSIER, &spiPorts[port].dmaRegs->dmatsr, caie, 1);    /*disable nrip interrupt */
#else
    physTx = (unsigned long) writeBuffer;
    physRx = (unsigned long) readBuffer;

    /*
     * setup buffer descriptor
     */

    for (i = 0; i < NASPI_MAX_DESCRIPTORS; i++)
    {
        /*Setup the tx buffer descriptor, and use the physical address in the DMA buffer descriptor */
        spiPorts[port].tx_buffer_descriptors[i].lower_word.bits.sbp = physTx + i * NASPI_MAX_DMABUFFER_LENGTH;
        spiPorts[port].rx_buffer_descriptors[i].lower_word.bits.sbp = physRx + i * NASPI_MAX_DMABUFFER_LENGTH;

        if (byteLeft <= NASPI_MAX_DMABUFFER_LENGTH)
        {
            spiPorts[port].tx_buffer_descriptors[i].upper_word.bits.buflen = byteLeft;
            spiPorts[port].rx_buffer_descriptors[i].upper_word.bits.buflen = byteLeft;
            spiPorts[port].tx_buffer_descriptors[i].upper_word.bits.full = 1;
            spiPorts[port].rx_buffer_descriptors[i].upper_word.bits.full = 0;
            spiPorts[port].tx_buffer_descriptors[i].lower_word.bits.last = 1;
            spiPorts[port].rx_buffer_descriptors[i].lower_word.bits.last = 1;
            spiPorts[port].tx_buffer_descriptors[i].lower_word.bits.intr = 1;
            spiPorts[port].rx_buffer_descriptors[i].lower_word.bits.intr = 1;
            spiPorts[port].tx_buffer_descriptors[i].lower_word.bits.wrap = 1;
            spiPorts[port].rx_buffer_descriptors[i].lower_word.bits.wrap = 1;
            break;
        }
        else
        {
            spiPorts[port].tx_buffer_descriptors[i].upper_word.bits.buflen = NASPI_MAX_DMABUFFER_LENGTH;
            spiPorts[port].rx_buffer_descriptors[i].upper_word.bits.buflen = NASPI_MAX_DMABUFFER_LENGTH;
            spiPorts[port].tx_buffer_descriptors[i].upper_word.bits.full = 1;
            spiPorts[port].rx_buffer_descriptors[i].upper_word.bits.full = 0;
            spiPorts[port].tx_buffer_descriptors[i].lower_word.bits.last = 0;
            spiPorts[port].rx_buffer_descriptors[i].lower_word.bits.last = 0;
            spiPorts[port].tx_buffer_descriptors[i].lower_word.bits.intr = 0;
            spiPorts[port].rx_buffer_descriptors[i].lower_word.bits.intr = 0;
            spiPorts[port].tx_buffer_descriptors[i].lower_word.bits.wrap = 0;
            spiPorts[port].rx_buffer_descriptors[i].lower_word.bits.wrap = 0;
        }

        byteLeft -= NASPI_MAX_DMABUFFER_LENGTH;

    }

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

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

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

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

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

    /* 
     * setup DMA status/interrupt register
     */
    narm_write_reg (NARM_DSIERSI_REG, &spiPorts[port].dmaRegs->dmarsr, ncie, 0);        /*use NRIP */
    narm_write_reg (NARM_DSIERSI_REG, &spiPorts[port].dmaRegs->dmatsr, ncie, 0);        /*use NRIP */

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

    narm_write_reg (NARM_DSIERSI_REG, &spiPorts[port].dmaRegs->dmarsr, nrie, 1);        /*disable nrip interrupt */
    narm_write_reg (NARM_DSIERSI_REG, &spiPorts[port].dmaRegs->dmatsr, nrie, 1);        /*disable nrip interrupt */

    narm_write_reg (NARM_DSIERSI_REG, &spiPorts[port].dmaRegs->dmarsr, caie, 0);        /*disable nrip interrupt */
    narm_write_reg (NARM_DSIERSI_REG, &spiPorts[port].dmaRegs->dmatsr, caie, 0);        /*disable nrip interrupt */
#endif

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

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

}


/*
 * This routine enable serial port and TX/RX DMA
 */
static void enablePort (int port)
{

    /* clean the interrupt flag */
    spiPorts[port].tx_int_flag = 0;
    spiPorts[port].rx_int_flag = 0;

    /*
     * Enable interrupt
     */
    spiMasterEnableIsr (spiPorts[port].rx_int_id);
    spiMasterEnableIsr (spiPorts[port].tx_int_id);

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

    /*
     * Enable dma
     */
#ifdef BSP_ARM9
    narm_write_reg (NA_DMA_DCR, &spiPorts[port].dmaRegs->dmarcr, ce, 1);        /*enable DMA operation */
    narm_write_reg (NA_DMA_DCR, &spiPorts[port].dmaRegs->dmatcr, ce, 1);        /*enable DMA operation */
#else
    narm_write_reg (NARM_DCR_REG, &spiPorts[port].dmaRegs->dmarcr, ce, 1);      /*enable DMA operation */
    narm_write_reg (NARM_DCR_REG, &spiPorts[port].dmaRegs->dmatcr, ce, 1);      /*enable DMA operation */
#endif

}


/*
 * This routine disable serial port and TX/RX DMA
 */
#ifndef AVERY_SPI_USAGE
static void disablePort (int port)
{
    /*
     * disable interrupt
     */
    spiMasterDisableIsr (spiPorts[port].rx_int_id);
    spiMasterDisableIsr (spiPorts[port].tx_int_id);

    /*
     * Disable dma
     */
#ifdef BSP_ARM9
    narm_write_reg (NA_DMA_DCR, &spiPorts[port].dmaRegs->dmarcr, ce, 0);
    narm_write_reg (NA_DMA_DCR, &spiPorts[port].dmaRegs->dmatcr, ce, 0);
#else
    narm_write_reg (NARM_DCR_REG, &spiPorts[port].dmaRegs->dmarcr, ce, 0);
    narm_write_reg (NARM_DCR_REG, &spiPorts[port].dmaRegs->dmatcr, ce, 0);
#endif

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

    /* clean the interrupt flag */
    spiPorts[port].tx_int_flag = 0;
    spiPorts[port].rx_int_flag = 0;

}
#endif /* AVERY_SPI_USAGE */

/* 
 * This routine setup GPIO pin as general GPIO output pin and
 * set the pin to be 0 or 1 according to the settings in device table
 */
#ifndef AVERY_SPI_USAGE
static void selectCs (NASpiDeviceConfigType * spiDevice)
{
    if (spiDevice->csAssertFn)
    {
        spiDevice->csAssertFn ();
    }
    else
    {
#ifdef BSP_ARM9
        NAconfigureGPIOpin (spiPorts[spiDevice->port].defaultCsPin, NA_GPIO_OUTPUT_STATE, 0);      /*enable bit as GPIO output */
#else
        NAconfigureGPIOpin (spiPorts[spiDevice->port].defaultCsPort, spiPorts[spiDevice->port].defaultCsPin, NA_GPIO_OUTPUT_STATE);     /*enable bit as GPIO output */
        NAsetGPIOpin (spiPorts[spiDevice->port].defaultCsPort, spiPorts[spiDevice->port].defaultCsPin, 0);
#endif
    }
}
#endif /* AVERY_SPI_USAGE */

/*
 * deselect the pin to be 0 or 1 according to the settings in device table
 */
#ifndef AVERY_SPI_USAGE
static void deselectCs (NASpiDeviceConfigType * spiDevice)
{
    if (spiDevice->csDessertFn)
    {
        spiDevice->csDessertFn ();
    }
    else
    {
#ifdef BSP_ARM9
        NAconfigureGPIOpin (spiPorts[spiDevice->port].defaultCsPin, NA_GPIO_OUTPUT_STATE, 1);      /*enable bit as GPIO output */
#else
        NAconfigureGPIOpin (spiPorts[spiDevice->port].defaultCsPort, spiPorts[spiDevice->port].defaultCsPin, NA_GPIO_OUTPUT_STATE);     /*enable bit as GPIO output */
        NAsetGPIOpin (spiPorts[spiDevice->port].defaultCsPort, spiPorts[spiDevice->port].defaultCsPin, 1);
#endif
    }
}
#endif /* AVERY_SPI_USAGE */

/*
 * This routine is the RX isr
 */
static void spi_rx_dma_int (int port)
{
#ifdef NS9360
#define RECEIVE_MASK  DMA_NRIP_MASK
#else
#define RECEIVE_MASK  DMA_NCIP_MASK
#endif
    unsigned long status = spiPorts[port].dmaRegs->dmarsr;

    /* Acknowledge interrupt */
    spiPorts[port].dmaRegs->dmarsr |= status;
    spiPorts[port].dmaRegs->dmarcr = 0;

    if ((status & DMA_ECIP_MASK) || (status & DMA_CAIP_MASK))
    {
        netosFatalError ("spi rx DMA error", 1, 1);
    }

    if ((status & RECEIVE_MASK) == RECEIVE_MASK)
    {
        spiPorts[port].rx_int_flag = 1;
    }

    spiPorts[port].rx_int_cnt++;

}


/*
 * This routine is the TX isr
 */
static void spi_tx_dma_int (int port)
{
#ifdef BSP_ARM9
#define TRANSMIT_MASK (DMA_NRIP_MASK)
#else
#define TRANSMIT_MASK (DMA_NCIP_MASK)
#endif
    unsigned long status = spiPorts[port].dmaRegs->dmatsr;

    /* Acknowledge interrupt */
    spiPorts[port].dmaRegs->dmatsr |= status;
    spiPorts[port].dmaRegs->dmatcr = 0;

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

    if ((status & TRANSMIT_MASK) == TRANSMIT_MASK)
    {
        spiPorts[port].tx_int_flag = 1;
    }

    spiPorts[port].tx_int_cnt++;

}

/*
 * This function checks if the named device is in spi master device table, and is 
 * used for serial flash driver only
 */
int deviceExist (char *name)
{
    INTERRUPT_SAVE_AREA;
    int i;

    DISABLE_INTS;
    for (i = 0; i < NASPI_MAX_DEVICE; i++)
    {
        if (spiDeviceTable[i].registered && !spiDeviceTable[i].inuse && !strcmp (name, spiDeviceTable[i].device.name))
        {
            RESTORE_INTS;
            return 1;
        }
    }
    RESTORE_INTS;
    return 0;

}

#ifndef BSP_ARM9

/*
 * On NS7520, SPI port 0 enable uses GPIO pin PORTA0, SPI port 1 enable uses GPIO pin PORTC0
 * On NET50-bga, SPI port 0 enable uses GPIO pin PORTC7, SPI port 1 enable uses GPIO pin PORTC5
 */
static void NAconfigureGPIOpin (int port, int pinNum, int state)
{
#define CSF                  1
    volatile unsigned long *portAddress;
    int chipRevision = narm_read_reg (NARM_GSSR_REG, NARM_GSSR_ADDR, rev);

    if (chipRevision != NETA50_4)
    {
        portAddress = (volatile unsigned long *) (0xFFB00020 + port * 8);

        switch (state)
        {
        case NA_GPIO_FUNC0_STATE:
            *portAddress |= ((1 << pinNum) << 24) | ((1 << pinNum) << 16) | (port ? ((CSF << pinNum) << 8) : 0);
            break;
        case NA_GPIO_FUNC1_STATE:
            *portAddress |= ((1 << pinNum) << 24) | (port ? ((CSF << pinNum) << 8) : 0);
            *portAddress &= ~((1 << pinNum) << 16);
            break;
        case NA_GPIO_INPUT_STATE:
            *portAddress &= ~(((1 << pinNum) << 24) | ((1 << pinNum) << 16) | (port ? ((CSF << pinNum) << 8) : 0));
            break;
        case NA_GPIO_OUTPUT_STATE:
            *portAddress |= ((1 << pinNum) << 16);
            *portAddress &= ~(((1 << pinNum) << 24) | (port ? ((CSF << pinNum) << 8) : 0));
            break;
        }
    }
    else
    {
        portAddress = (volatile unsigned long *) (0xFFB00020 + port * 4);

        switch (state)
        {
        case NA_GPIO_FUNC0_STATE:
            *portAddress |= ((1 << pinNum) << 24) | ((1 << pinNum) << 16);
            break;
        case NA_GPIO_FUNC1_STATE:
            *portAddress |= (1 << pinNum) << 24;
            *portAddress &= ~((1 << pinNum) << 16);
            break;
        case NA_GPIO_INPUT_STATE:
            *portAddress &= ~(((1 << pinNum) << 24) | ((1 << pinNum) << 16));
            break;
        case NA_GPIO_OUTPUT_STATE:
            *portAddress |= ((1 << pinNum) << 16);
            *portAddress &= ~((1 << pinNum) << 24);
            break;
        }
    }
}

/*
 * On NS7520, SPI port 0 enable uses GPIO pin PORTA0, SPI port 1 enable uses GPIO pin PORTC0
 * On NET50-bga, SPI port 0 enable uses GPIO pin PORTC7, SPI port 1 enable uses GPIO pin PORTC5
 */
static void NAsetGPIOpin (int port, int pinNum, int state)
{
    volatile unsigned long *portAddress;
    int chipRevision = narm_read_reg (NARM_GSSR_REG, NARM_GSSR_ADDR, rev);

    if (chipRevision != NETA50_4)
        portAddress = (volatile unsigned long *) (0xFFB00020 + port * 8);
    else
        portAddress = (volatile unsigned long *) (0xFFB00020 + port * 4);

    if (state)
    {
        *portAddress |= (1 << pinNum);
    }
    else
    {
        *portAddress &= ~(1 << pinNum);
    }
}
#endif

/*
 *                                                                          
 * Function Name:  configBaudDivisor()                
 *                                                 
 * Called by:      setupSpiPort()                           
 *                                                                          
 * Comments: This routine sets up the baud rate divisor which is required  
 * to set up the hardware for the desired baudrate. 
 *                                                                          
 * This function calculates the optimum N value. For NS7520 and NET50gba, the divisor 
 * is based on the values of SysClk or Xtal frequency; for NS9750, NS9360, the divisor 
 * is based on the values of BBUS clock frequency.
 *
 * 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 in this routine.
 *
 * If the actual speed is desired, please use the equation provided in the Hardware 
 * Reference Guide, on Serial Bit-Rate Registers.
 *
 * This routine is modified from the copp from the legacy NETOS SPI driver.
 */
static void configBaudDivisor (int port, int baudrate)
{
#ifdef BSP_ARM9
    unsigned int Nbbusclk;

    Nbbusclk = ((NAgetBbusClkFreq () / (baudrate * 2)) - 1);
    if ((int)((NAgetBbusClkFreq () / (2 * (Nbbusclk + 1)) - baudrate)) > baudrate / 2)
        Nbbusclk++;

    if (Nbbusclk > 0x3fff)      /*  The n parameter can not be greater than 15 bits of storage  */
        Nbbusclk = 0x3fff;

    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, clkmux, 1);    /*using BCLKfor arm9 */
    narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, nreg, Nbbusclk);
#else
    unsigned long Nsysclk, Nxtal, Esysclk, Extal, sysClkFreq, xtalFreq, xmode;

    sysClkFreq = NAgetCpuClkFreq ();
    xtalFreq = NAgetSysOscFreq ();

    sysClkFreq = NAgetCpuClkFreq ();
    xtalFreq = NAgetSysOscFreq ();

    /*  compute the N value for sysclk  */
    Nsysclk = (sysClkFreq / (2 * baudrate)) - 1;

    /*  check to round up   */
    if ((sysClkFreq % (2 * baudrate)) > (unsigned int) 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 + 1))) >  (unsigned int)baudrate)
        {
            Esysclk = (sysClkFreq / (2 * (Nsysclk + 1))) - baudrate;
        }
        else
        {
            Esysclk = baudrate - (sysClkFreq / (2 * (Nsysclk + 1)));
        }

    }

    /*  compute the N value for xtal  */
    if (narm_read_reg (NARM_GSSR_REG, NARM_GSSR_ADDR, rev) != NETA50_4)
        xmode = 1;              /*NS7520 */
    else
        xmode = 16;             /*NET50 */

    Nxtal = (xtalFreq / (2 * xmode * baudrate)) - 1;

    /*  check to round up   */
    if ((xtalFreq % (2 * xmode * baudrate)) >  (unsigned int) baudrate)
    {
        /*  remainder exists     */
        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 * xmode * (Nxtal + 1))) >  (unsigned int) baudrate)
        {
            Extal = (xtalFreq / (2 * xmode * (Nxtal + 1))) - baudrate;
        }
        else
        {
            Extal = baudrate - (xtalFreq / (2 * xmode * (Nxtal + 1)));
        }
    }
    /*  pick the best scenario  */
    if (Esysclk < Extal)
    {
        /*  sysclk error less than or equal to XTAL clock  */
        narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, clkmux, 1);        /* sysclk */
        narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, nreg, Nsysclk);
    }
    else
    {
        /*  sysclk error greater than XTAL clock  */
        narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, clkmux, 0);        /* Xtale */
        narm_write_reg (NARM_SBRG_REG, &spiPorts[port].spiRegs->brg, nreg, Nxtal);
    }
#endif
}


