/**
 *  \file   main_mcspi_example.c
 *
 *
 */

/*
 * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * Neither the name of Texas Instruments Incorporated nor the names of
 * its contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */


/* XDCtools Header files */
#include <xdc/std.h>
#include <xdc/cfg/global.h>
#include <xdc/runtime/System.h>
#include <stdio.h>
#include <ti/sysbios/knl/Task.h>

/* BIOS Header files */
#include <ti/sysbios/BIOS.h>
#include <xdc/runtime/Error.h>

/* TI-RTOS Header files */
#include <ti/drv/spi/SPI.h>
#include <ti/drv/spi/test/src/SPI_log.h>
#include <ti/drv/spi/test/src/SPI_board.h>
#include <ti/drv/spi/soc/SPI_v1.h>
#include <ti/csl/csl_mcspi.h>

#include <ti/csl/soc.h>
#include <ti/csl/hw_types.h>


#include <ti/board/board.h>

/**********************************************************************
 ************************** Macros ************************************
 **********************************************************************/


/**********************************************************************
 ************************** Internal functions ************************
 **********************************************************************/
/* Delay function */
static void delay(unsigned int delayValue);



/* Callback function to be called in callback mode */
void MCSPICallbackFxn(SPI_Handle handle, SPI_Transaction * transaction);

/* Function to verify data */
bool VerifyData(uint8_t *expData, uint8_t *rxData, uint32_t length);

#if defined(SOC_AM572x) || defined (SOC_AM571x)
extern void  MCSPI_Board_crossbarInit(void);
#endif

#if defined(SOC_AM572x) || defined (SOC_AM571x)
#if defined (__TI_ARM_V7M4__)
#define DELAY_VALUE       (100U)
#else
#define DELAY_VALUE       (1000U)
#endif
#else
#define DELAY_VALUE       (1000U)
#endif

/**********************************************************************
 ************************** Global Variables **************************
 **********************************************************************/

/* Reference Serializer data */
extern char serializerData[1];

/* Buffer containing the known data that needs to be written to flash */
uint8_t txBuf[1U];

/* Buffer containing the received data */
uint8_t rxBuf[1U] = {0xFFU};

/* Flag to check transfer completion in callback mode */
uint32_t txCompleteCallbackFlag = 1U;

/* Transfer length */
uint32_t transferLength;



/*
 *  ======== SPI init config ========
 */
static void SPI_initConfig(uint32_t instance, bool multiChn)
{

    SPI_v1_HWAttrs spi_cfg;

	bool        pollMode = true;
#ifdef SPI_DMA_ENABLE
    bool        dmaMode = true;
#endif

    /* Get the default SPI init configurations */
    SPI_socGetInitCfg(instance, &spi_cfg);

    /*
     * Set blocking mode (dma mode or non-dma interrupt mode)
     * or callback mode
     */
    if (pollMode == true)
    {
        /* polling mode */
        spi_cfg.enableIntr = false;
    }
    else
    {
        /* interrupt enabled */
        spi_cfg.enableIntr = true;
#ifdef SPI_DMA_ENABLE
        if (dmaMode == true)
        {
            /* Set the DMA related init config */
            spi_cfg.edmaHandle = MCSPIApp_edmaInit();
            spi_cfg.dmaMode    = TRUE;
        }
        else
#endif
        {
            spi_cfg.edmaHandle = NULL;
            spi_cfg.dmaMode    = FALSE;
        }
    }
    testChNum = spi_cfg.chNum;
    spi_cfg.chMode = MCSPI_MULTI_CH;

    /* Set the SPI init configurations */
    SPI_socSetInitCfg(instance, &spi_cfg);
}


/*
 *  ======== spi_test_transfer ========
 */
bool spi_test_transfer(void *spi, bool cbMode, uint32_t timeout, bool dmaMode, bool multChn)
{
    bool retVal;
    SemaphoreP_Handle sem;

    /* Load data */

    /* Initiate transfer */
    txBuf[0] = 0xAAU;
    memset(rxBuf, 0, MCSPI_XFER_LEN);

    transaction.status= SPI_TRANSFER_STARTED;
    transaction.count = MCSPI_XFER_LEN;
    transaction.txBuf = &txBuf[0];
    transaction.rxBuf = &rxBuf[0];

#ifdef SPI_DMA_ENABLE
    if (dmaMode)
    {
        CacheP_wb((void *)txBuf, (int32_t)MCSPI_XFER_LEN);
        CacheP_wb((void *)rxBuf, (int32_t)MCSPI_XFER_LEN);
    }
#endif


        retVal = MCSPI_transfer((MCSPI_Handle)spi, &transaction);
  

#ifdef SPI_DMA_ENABLE
    if (dmaMode == true)
    {
        CacheP_Inv((void *)rxBuf, (int32_t)MCSPI_XFER_LEN);
    }
#endif

    if(false == retVal)
    {
        printf("\n Error occurred in spi transfer \n");
        goto Err;
    }
    else
    {
        /**
         *  wait for the semaphore is posed in the callback mode to indicate the
         *  transfer completion.
         */
        if(cbMode == true)
        {
            if (multChn == true)
            {
                sem = cbSem[testChNum];
            }
            else
            {
                sem = cbSem[0];
            }
            if (SPI_osalPendLock(sem, timeout) != SemaphoreP_OK)
            {
                goto Err;
            }
        }

        retVal = VerifyData((uint8_t *)&serializerData[0], &rxBuf[0], MCSPI_XFER_LEN);

    }
    printf("\n SPI_Transfer returned transaction status = %d\n",transaction.status);

Err:
    return (retVal);
}


/*
 *  ======== test function ========
 */
bool spi_test_multiple_channel(void *arg)
{
    MCSPI_Params      mcSpiParams;
    MCSPI_Handle      spi[MCSPI_MAX_NUM_CHN];
    MCSPI_CallbackFxn cbFxn[MCSPI_MAX_NUM_CHN] = {MCSPI_callback0, MCSPI_callback1, MCSPI_callback2, MCSPI_callback3};
    uint32_t          instance, chn;
    bool              retVal;
    SPI_Tests        *test = (SPI_Tests *)arg;
    bool              cbMode = false;
    bool              dmaMode = false;
    uint32_t          timeout = SemaphoreP_WAIT_FOREVER;

    if (cbMode == true)
    {
        /* Create call back semaphore */
        SPI_osalSemParamsInit(&cbSemParams);
        cbSemParams.mode = SemaphoreP_Mode_BINARY;
    }

    instance = BOARD_MCSPI_SERIALIZER_INSTANCE;
    SPI_initConfig(instance, true);

    /* Default SPI configuration parameters */
    MCSPI_Params_init(&mcSpiParams);
    mcSpiParams.frameFormat  = SPI_POL1_PHA0;
    mcSpiParams.transferTimeout = timeout;
    if (cbMode)
    {
        mcSpiParams.transferMode = SPI_MODE_CALLBACK;
    }

    /* Open the default channel first */
    chn = testChNum;
    if (cbMode == true)
    {
        cbSem[chn] = SPI_osalCreateBlockingLock(0, &cbSemParams);
        mcSpiParams.transferCallbackFxn = cbFxn[chn];
    }
    spi[chn] = MCSPI_open(instance, chn, &mcSpiParams);
    if (spi[chn] == NULL)
    {
        printf("Error initializing SPI");
        goto Err;
    }
    else
    {
        printf("SPI instance %d channel %d initialized\n",
                instance, chn);
    }

    for (chn = 0; chn < MCSPI_MAX_NUM_CHN; chn++)
    {
        if (chn == testChNum) continue;
        if (cbMode == true)
        {
            cbSem[chn] = SPI_osalCreateBlockingLock(0, &cbSemParams);
            mcSpiParams.transferCallbackFxn = cbFxn[chn];;
        }

        spi[chn] = MCSPI_open(instance, chn, &mcSpiParams);

        if (spi[chn] == NULL)
        {
            printf("Error initializing SPI");
            goto Err;
        }
        else
        {
            printf("SPI instance %d channel %d initialized\n",
                    instance, chn);
        }
    }

    retVal = spi_test_transfer((void *)spi[testChNum], cbMode, timeout, dmaMode, true);

Err:
    for (chn = 0; chn < MCSPI_MAX_NUM_CHN; chn++)
    {
        if (spi[chn])
        {
            MCSPI_close(spi[chn]);
        }

        if (cbSem[chn])
        {
            SPI_osalDeleteBlockingLock(cbSem[chn]);
            cbSem[chn] = NULL;
        }
    }

    return (retVal);
}

/*
 *  ======== main ========
 */
int main(void)
{
    /* Call board init functions */
    Board_initCfg boardCfg;
  boardCfg = BOARD_INIT_PINMUX_CONFIG |
        BOARD_INIT_MODULE_CLOCK |
        BOARD_INIT_UART_STDIO;
    Board_init(boardCfg);


    Task_Handle task;
    Error_Block eb;

    Error_init(&eb);

    task = Task_create(spi_test_multiple_channel, NULL, &eb);
        if (task == NULL) {
            System_printf("Task_create() failed!\n");
            BIOS_exit(0);
        }

#if defined(SOC_AM572x) || defined (SOC_AM571x)
    MCSPI_Board_crossbarInit();
#endif

    /* Start BIOS */
    BIOS_start();
    return (0);
}





/*
 *  ======== Delay function ========
 */
void delay(unsigned int delayValue)
{
    volatile uint32_t delay1 = delayValue*10000;
    while (delay1--) ;
}

/*
 *  ======== Callback function ========
 */
void MCSPICallbackFxn(SPI_Handle handle, SPI_Transaction * transaction)
{
    txCompleteCallbackFlag = 0;
}

/*
 *  ======== CompareData ========
 */
bool VerifyData(uint8_t *expData, uint8_t *rxData, uint32_t length)
{
    uint32_t idx = 0;
    uint32_t match = 1;
    bool retVal = false;

    for(idx = 0; ((idx < length) && (match != 0)); idx++)
    {
        if(*expData != *rxData) match = 0;
        expData++;
        rxData++;
    }

    if(match == 1) retVal = true;

    return retVal;
}
