#include <xdc/runtime/Memory.h>

#include <ti/csl/csl_chip.h>
#include <ti/csl/csl_semAux.h>
#include <ti/csl/cslr_device.h>

#include "ti/drv/rm/test/rm_test.h"
#include "ti/drv/cppi/cppi_drv.h"

#define NUM_HOST_DESC               32
#define SIZE_HOST_DESC              48
#define NUM_MONOLITHIC_DESC         32
#define SIZE_MONOLITHIC_DESC        160

#define NUM_DATA_BUFFER             32
#define SIZE_DATA_BUFFER            64

#define     CPPI_HW_SEM         4 

/* CPPI device specific configuration */
extern Cppi_GlobalConfigParams  cppiGblCfgParams;

Void rmTestRxChannelOpen (Cppi_Handle cppiHnd, rm_test_expect_e expect)
{
    UInt8               isAllocated;
    Cppi_ChHnd          ChHnd;
    Cppi_RxChInitCfg    ChCfg;

    /* Don't specify channel number and let CPPI allocate the next available one */    
    ChCfg.channelNum = CPPI_PARAM_NOT_SPECIFIED;
    ChCfg.rxEnable = Cppi_ChState_CHANNEL_ENABLE;

    /* Open Channel */
    ChHnd = (Cppi_ChHnd) Cppi_rxChannelOpen (cppiHnd, &ChCfg, &isAllocated);
    if (ChHnd == NULL)
    {
        RM_PRINT_RESULT("Cppi_rxChannelOpen", expect, rm_denied, (uint32_t) ChHnd);
    }
    else
    {
        RM_PRINT_RESULT("Cppi_rxChannelOpen", expect, rm_granted, (uint32_t) ChHnd);
        Cppi_channelClose (ChHnd);
    }

}

Void rmTestTxChannelOpen(Cppi_Handle cppiHnd, rm_test_expect_e expect)
{
    UInt8               isAllocated;
    Cppi_ChHnd          ChHnd;
    Cppi_TxChInitCfg    ChCfg;

    /* Don't specify channel number and let CPPI allocate the next available one */    
    ChCfg.channelNum = CPPI_PARAM_NOT_SPECIFIED;
    ChCfg.priority = 2;
    ChCfg.filterEPIB = 1;
    ChCfg.filterPS = 1;
    ChCfg.aifMonoMode = 1;
    ChCfg.txEnable = Cppi_ChState_CHANNEL_ENABLE;

    /* Open Channel */
    ChHnd = (Cppi_ChHnd) Cppi_txChannelOpen (cppiHnd, &ChCfg, &isAllocated);
    if (ChHnd == NULL)
    {
        RM_PRINT_RESULT("Cppi_txChannelOpen", expect, rm_denied, (uint32_t) ChHnd);
    }
    else
    {
        RM_PRINT_RESULT("Cppi_txChannelOpen", expect, rm_granted, (uint32_t) ChHnd);
        Cppi_channelClose (ChHnd);
    }

}

Void rmTestRxFlowConfig (Cppi_Handle cppiHnd, rm_test_expect_e expect)
{
    UInt8               isAllocated;
    Cppi_FlowHnd        rxFlowHnd;
    Cppi_RxFlowCfg      rxFlowCfg;

    memset ((Void *) &rxFlowCfg, 0, sizeof (Cppi_RxFlowCfg));

#ifdef _TCI6614_Atrenta_DSP1_H_
    /* Request flow number 12 - 0 through 11 are given to ARM */
    rxFlowCfg.flowIdNum = 12;
#else  /* All other devices without an ARM */
    /* Request flow number 0 */
    rxFlowCfg.flowIdNum = 0;
#endif
    
    /* Open Rx Flow */

    rxFlowHnd = (Cppi_FlowHnd) Cppi_configureRxFlow (cppiHnd, &rxFlowCfg, &isAllocated);
    if (rxFlowHnd == NULL) {
        RM_PRINT_RESULT("Cppi_configureRxFlow", expect, rm_denied, (uint32_t) rxFlowHnd);
    }
    else {
        RM_PRINT_RESULT("Cppi_configureRxFlow", expect, rm_granted, (uint32_t) rxFlowHnd);
        Cppi_closeRxFlow (rxFlowHnd);
    }
}

void rm_test_cppi(Rm_Handle rm_handle, rm_test_expect_e expect)
{
    Cppi_CpDmaInitCfg   cpdmaCfg;
    Cppi_Handle         cppiHnd;
    Cppi_StartCfg       startCfg;
    Cppi_Result         result;

    System_printf ("~~~~~~~~~~~~~Core %d RM CPPI TEST START~~~~~~~~~~~~~~~~\n", DNUM);

    if (DNUM == 0) {
        /* Initialize CPPI LLD */
        result = Cppi_init (&cppiGblCfgParams);
        if (result != CPPI_SOK)
        {
            System_printf ("Error Core %d : Initializing CPPI LLD error code : %d\n", DNUM, result);
        }
    }

    memset ((Void *) &cpdmaCfg, 0, sizeof (Cppi_CpDmaInitCfg));
    cpdmaCfg.dmaNum = Cppi_CpDma_QMSS_CPDMA;
    cpdmaCfg.writeFifoDepth = 32;
    cpdmaCfg.timeoutCount = 0x7F;
    cpdmaCfg.qm0BaseAddress = 0x34020000;

    cppiHnd = (Cppi_Handle) Cppi_open (&cpdmaCfg);
    if (cppiHnd == NULL)
    {
        System_printf ("Error Core %d : Initializing SRIO CPPI CPDMA %d\n", DNUM, cpdmaCfg.dmaNum);
        return;
    }

    startCfg.rmHandle = rm_handle;

    Cppi_startCfg(&startCfg);

    rmTestRxChannelOpen(cppiHnd, expect);

    rmTestTxChannelOpen (cppiHnd, expect);

    rmTestRxFlowConfig(cppiHnd, expect);

    System_printf ("~~~~~~~~~~~~~Core %d RM CPPI TEST DONE~~~~~~~~~~~~~~~~\n", DNUM);

}

UInt32      cppiMallocCounter   = 0;
UInt32      cppiFreeCounter     = 0;
extern UInt32      coreKey [MAX_NUM_CORES];

/**
 *  @b Description
 *  @n  
 *      The function is used to allocate a memory block of the specified size.
 *
 *      Note: If the LLD is used by applications on multiple core, the "cppiHeap"
 *      should be in shared memory
 *
 *  @param[in]  num_bytes
 *      Number of bytes to be allocated.
 *
 *  @retval
 *      Allocated block address
 */
Ptr Osal_cppiMalloc (UInt32 num_bytes)
{
	Error_Block	errorBlock;
    Ptr dataPtr;

    /* Increment the allocation counter. */
    cppiMallocCounter++;	

	/* Allocate memory. */
    dataPtr = Memory_alloc(NULL, num_bytes, 0, &errorBlock);
	return (dataPtr);
}

/**
 *  @b Description
 *  @n  
 *      The function is used to free a memory block of the specified size allocated 
 *      using Osal_cppiMalloc() API.
 *
 *  @param[in]  ptr
 *      Pointer to the memory block to be cleaned up.
 *
 *  @param[in]  size
 *      Size of the memory block to be cleaned up.
 *
 *  @retval
 *      Not Applicable
 */
Void Osal_cppiFree (Ptr ptr, UInt32 size)
{
    /* Increment the free counter. */
    cppiFreeCounter++;	
	Memory_free (NULL, ptr, size);
}

/**
 * ============================================================================
 *  @n@b Osal_cppiCsEnter
 *
 *  @b  brief
 *  @n  This API ensures multi-core and multi-threaded
 *      synchronization to the caller.
 *
 *      This is a BLOCKING API.
 *
 *      This API ensures multi-core synchronization between
 *      multiple processes trying to access CPPI shared
 *      library at the same time.
 *
 *  @param[in]  
 *  @n  None
 *
 *  @return     
 *  @n  Handle used to lock critical section
 * =============================================================================
 */
Ptr Osal_cppiCsEnter (Void)
{
    /* Get the hardware semaphore. 
     *
     * Acquire Multi core CPPI synchronization lock 
     */
    while ((CSL_semAcquireDirect (CPPI_HW_SEM)) == 0);

    /* Disable all interrupts and OS scheduler. 
     *
     * Acquire Multi threaded / process synchronization lock.
     */
    coreKey [CSL_chipReadReg (CSL_CHIP_DNUM)] = Hwi_disable();

    return NULL;
}

/**
 * ============================================================================
 *  @n@b Osal_cppiCsExit
 *
 *  @b  brief
 *  @n  This API needs to be called to exit a previously
 *      acquired critical section lock using @a Osal_cppiCsEnter ()
 *      API. It resets the multi-core and multi-threaded lock,
 *      enabling another process/core to grab CPPI access.
 *
 *  @param[in]  CsHandle
 *      Handle for unlocking critical section.
 *
 *  @return     None
 * =============================================================================
 */
Void Osal_cppiCsExit (Ptr CsHandle)
{
    /* Enable all interrupts and enables the OS scheduler back on.
     *
     * Release multi-threaded / multi-process lock on this core.
     */
    Hwi_restore(coreKey [CSL_chipReadReg (CSL_CHIP_DNUM)]);

    /* Release the hardware semaphore 
     *
     * Release multi-core lock.
     */ 
    CSL_semReleaseSemaphore (CPPI_HW_SEM);

    return;
}

/**
 *  @b Description
 *  @n  
 *      The function is the CPPI OSAL Logging API which logs 
 *      the messages on the console.
 *
 *  @param[in]  fmt
 *      Formatted String.
 *
 *  @retval
 *      Not Applicable
 */
Void Osal_cppiLog ( String fmt, ... )
{
}

/**
 *  @b Description
 *  @n  
 *      The function is used to indicate that a block of memory is 
 *      about to be accessed. If the memory block is cached then this 
 *      indicates that the application would need to ensure that the 
 *      cache is updated with the data from the actual memory.
 *
 *  @param[in]  blockPtr
 *       Address of the block which is to be invalidated
 *
 *  @param[in]  size
 *       Size of the block to be invalidated

 *  @retval
 *      Not Applicable
 */
void Osal_cppiBeginMemAccess (void *blockPtr, uint32_t size)
{
    uint32_t    key;

    /* Disable Interrupts */
    key = Hwi_disable();

    /* Cleanup the prefetch buffer also. */
    CSL_XMC_invalidatePrefetchBuffer();
    
    SYS_CACHE_INV (blockPtr, size, CACHE_FENCE_WAIT);
    
    asm   (" nop      4");
    asm   (" nop      4");
    asm   (" nop      4");
    asm   (" nop      4");

    /* Reenable Interrupts. */
    Hwi_restore(key);
    
    return;
}

/**
 *  @b Description
 *  @n  
 *      The function is used to indicate that the block of memory has 
 *      finished being accessed. If the memory block is cached then the 
 *      application would need to ensure that the contents of the cache 
 *      are updated immediately to the actual memory. 
 *
 *  @param[in]  blockPtr
 *       Address of the block which is to be written back
 *
 *  @param[in]  size
 *       Size of the block to be written back

 *  @retval
 *      Not Applicable
 */
void Osal_cppiEndMemAccess (void *blockPtr, uint32_t size)
{
    uint32_t    key;

    /* Disable Interrupts */
    key = Hwi_disable();

    SYS_CACHE_WB (blockPtr, size, CACHE_FENCE_WAIT);

    asm   (" nop      4");
    asm   (" nop      4");
    asm   (" nop      4");
    asm   (" nop      4");

    /* Reenable Interrupts. */
    Hwi_restore(key);
    return;
}

