/**
 *   @file  qmss_test.c
 *
 *   @brief   
 *      This is the QMSS unit test code.
 *
 *  \par
 *  ============================================================================
 *  @n   (C) Copyright 2009-2011, Texas Instruments, Inc.
 * 
 *  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.
 *
 *  \par
*/

#include <xdc/std.h>
#include <xdc/runtime/System.h>
#include <string.h>

/* QMSS LLD include */
#include <ti/drv/qmss/qmss_drv.h>
#include <ti/drv/qmss/qmss_firmware.h>

/* CSL RL includes */
#include <ti/csl/csl_chip.h>

/* OSAL includes */
#include <qmss_osal.h>

/************************ USER DEFINES ********************/
#define  MEM_POOL_SIZE 256*1024

#define  DESC_SIZE          64
#define  REGION0_BASE(x)    x
#define  REGION0_NUM_DESCS  64
#define  REGION0_START_IDX  0
#define  REGION1_BASE(x)    (REGION0_BASE(x) + (REGION0_NUM_DESCS*DESC_SIZE))
#define  REGION1_NUM_DESCS  128
#define  REGION1_START_IDX  (REGION0_START_IDX+REGION0_NUM_DESCS)
#define  REGION2_BASE(x)    (REGION1_BASE(x) + (REGION1_NUM_DESCS*DESC_SIZE))
#define  REGION2_NUM_DESCS  64
#define  REGION2_START_IDX  (REGION1_START_IDX+REGION1_NUM_DESCS)


/************************ GLOBAL VARIABLES ********************/

/* Descriptor pool [Size of descriptor * Number of descriptors] */
#pragma DATA_ALIGN (memPool, 16)
UInt8                   memPool[MEM_POOL_SIZE];

/* Global variable common to all test cases */

/* QMSS configuration */
Qmss_InitCfg            qmssInitConfig;
/* Memory region configuration information */
Qmss_MemRegInfo         memInfo;
/* Memory region configuration status */
Qmss_MemRegCfg          memRegStatus;
/* QM descriptor configuration */
Qmss_DescCfg            descCfg;
/* Store the queue handle for destination queues on which allocated descriptors are stored */
Qmss_QueueHnd           QueHnd[QMSS_MAX_MEM_REGIONS];
/* Total tests */
UInt32                  testCount = 0;

/************************ EXTERN VARIABLES ********************/
/* Error counter */
extern UInt32                   errorCount;
/* QMSS device specific configuration */
extern Qmss_GlobalConfigParams  qmssGblCfgParams;

/**
 *  @b Description
 *  @n  
 *      Utility function which converts a local GEM L2 memory address 
 *      to global memory address.
 *
 *  @param[in]  addr
 *      Local address to be converted
 *
 *  @retval
 *      Computed L2 global Address
 */
static UInt32 l2_global_address (UInt32 addr)
{
    UInt32 corenum;

    /* Get the core number. */
    corenum = CSL_chipReadReg (CSL_CHIP_DNUM); 

    /* Compute the global address. */
    return (addr + (0x10000000 + (corenum * 0x1000000)));
}

static void validate (const char *msg, Qmss_Result expectedResult) 
{
    int corenum = CSL_chipReadReg(CSL_CHIP_DNUM); 
    Qmss_Result result;
    result = Qmss_insertMemoryRegion (&memInfo);

    if (expectedResult == QMSS_SOK)
    {
        if (result < QMSS_SOK) 
        {
            System_printf ("Error Core %d: %s: failure %d but expected success\n", 
                           corenum, msg, result);
            errorCount++;
        }
    } 
    else if (result != expectedResult)
    {
        System_printf ("Error Core %d: %s: got failure %d "
                       "but expected failure %d\n", 
                       corenum, msg, result, expectedResult);
        errorCount++;
    }
    testCount++;
}

Void testInsertMemRegion (Void)
{
    Qmss_Result             result;
    UInt32                  corenum;
    uint8_t                *memBase;
    Qmss_MemRegInfo         cleanMemInfoReg0;
    Qmss_MemRegInfo         cleanMemInfoReg1;
    Qmss_MemRegInfo         cleanMemInfoReg2;

    /* Get the core number. */
    corenum = CSL_chipReadReg(CSL_CHIP_DNUM); 
    System_printf ("**********Core %d TESTING DESCRIPTOR MEMORY REGIONS ************\n", corenum);

    memset ((Void *) &qmssInitConfig, 0, sizeof (Qmss_InitCfg));

    /* Set up the linking RAM. Use external Linking RAM. 
     * LLD will configure the internal linking RAM address and default size if a value of zero is specified.
     * Linking RAM1 is not used */
    qmssInitConfig.linkingRAM0Base = NULL;
    qmssInitConfig.linkingRAM0Size = 0;
    qmssInitConfig.linkingRAM1Base = 0x0;
    qmssInitConfig.maxDescNum      = QMSS_LINKING_RAM_REGION_0_DEFAULT_SIZE;

    /* Initialize Queue Manager SubSystem */
    result = Qmss_init (&qmssInitConfig, &qmssGblCfgParams);
    if (result != QMSS_SOK)
    {
        System_printf ("Error Core %d : Initializing Queue Manager SubSystem error code : %d\n", corenum, result);
        errorCount++;
        return;
    }

    /* Start Queue Manager SubSystem */
    result = Qmss_start ();
    if (result != QMSS_SOK)
    {
        System_printf ("Core %d : Error starting Queue Manager error code : %d\n", corenum, result);
    }

    /* Setup memory region for monolithic descriptors */
    memBase = (uint8_t *)l2_global_address ((UInt32) memPool);
    memset (&cleanMemInfoReg0, 0, sizeof(cleanMemInfoReg0));

    /* Set up template configuration using region 0 */
    cleanMemInfoReg0.descBase       = (uint32_t *)(REGION0_BASE(memBase));
    cleanMemInfoReg0.startIndex     = REGION0_START_IDX;
    cleanMemInfoReg0.descNum        = REGION0_NUM_DESCS;
    cleanMemInfoReg0.memRegion      = Qmss_MemRegion_MEMORY_REGION0;
    cleanMemInfoReg0.descSize       = DESC_SIZE;
    cleanMemInfoReg0.manageDescFlag = Qmss_ManageDesc_MANAGE_DESCRIPTOR;
    /* Set up template configuration using region 1 */
    cleanMemInfoReg1                = cleanMemInfoReg0;
    cleanMemInfoReg1.descBase       = (uint32_t *)(REGION1_BASE(memBase));
    cleanMemInfoReg1.startIndex     = REGION1_START_IDX;
    cleanMemInfoReg1.descNum        = REGION1_NUM_DESCS;
    cleanMemInfoReg1.memRegion      = Qmss_MemRegion_MEMORY_REGION1;
    /* Set up template configuration using region 2 */
    cleanMemInfoReg2                = cleanMemInfoReg0;
    cleanMemInfoReg2.descBase       = (uint32_t *)(REGION2_BASE(memBase));
    cleanMemInfoReg2.startIndex     = REGION2_START_IDX;
    cleanMemInfoReg2.descNum        = REGION2_NUM_DESCS;
    cleanMemInfoReg2.memRegion      = Qmss_MemRegion_MEMORY_REGION2;

    memInfo             = cleanMemInfoReg1;
    memInfo.descSize    = 1;
    validate ("Checking bad descriptor size (1)", 
              QMSS_INVALID_PARAM);

    memInfo             = cleanMemInfoReg1;
    memInfo.descSize    = 63;
    validate ("Checking bad descriptor size (63)", 
              QMSS_INVALID_PARAM);

    memInfo             = cleanMemInfoReg1;
    memInfo.descNum     = 1;
    validate ("Checking bad number of descriptors (1)", 
              QMSS_INVALID_PARAM);

    memInfo             = cleanMemInfoReg1;
    memInfo.descNum     = 1;
    validate ("Checking bad number of descriptors (63)", 
              QMSS_INVALID_PARAM);

    memInfo             = cleanMemInfoReg1;
    memInfo.descNum     = 96; // should be power of 2
    validate ("Checking bad number of descriptors (96)", 
              QMSS_INVALID_PARAM);

    memInfo             = cleanMemInfoReg1;
    memInfo.descBase++; 
    validate ("Checking unaligned descriptor base",
              QMSS_INVALID_PARAM);

    memInfo             = cleanMemInfoReg1;
    memInfo.descBase++; 
    validate ("Checking unaligned start index",
              QMSS_INVALID_PARAM);

    memInfo             = cleanMemInfoReg1;
    memInfo.memRegion   = (Qmss_MemRegion)QMSS_MAX_MEM_REGIONS; 
    validate ("Checking invalid memory region #",
              QMSS_MEMREGION_INVALID_INDEX);

    memInfo             = cleanMemInfoReg1;
    validate ("Good configuration of region 1",
              QMSS_SOK);

    validate ("Bad duplicate configuration into region 1",
              QMSS_MEMREGION_ALREADY_INITIALIZED);

    /* Now test errors regarding region 0 */
    memInfo             = cleanMemInfoReg0;
    memInfo.descBase    = (uint32_t *)(REGION1_BASE(memBase)) + 16;
    validate ("Test new region start overlap region 1",
              QMSS_MEMREGION_OVERLAP);
    
    memInfo             = cleanMemInfoReg0;
    memInfo.descBase    = (uint32_t *)(REGION1_BASE(memBase)) - 16;
    validate ("Test new region end overlap region 1",
              QMSS_MEMREGION_OVERLAP);
    
    memInfo             = cleanMemInfoReg0;
    memInfo.descBase    = (uint32_t *)(REGION1_BASE(memBase)) - 16;
    memInfo.descNum     = 256;
    validate ("Test region 1 base addr completely inside region 0",
              QMSS_MEMREGION_OVERLAP);

    memInfo             = cleanMemInfoReg0;
    memInfo.startIndex  = REGION1_START_IDX + 32;
    validate ("Test new region start index overlap region 1",
              QMSS_MEMREGION_OVERLAP);

    memInfo             = cleanMemInfoReg0;
    memInfo.startIndex  = REGION1_START_IDX - 32;
    validate ("Test new region end index overlap region 1",
              QMSS_MEMREGION_OVERLAP);

    memInfo             = cleanMemInfoReg0;
    memInfo.startIndex  = REGION1_START_IDX - 32;
    memInfo.descNum     = 256;
    validate ("Test region 1 index completely inside region 0",
              QMSS_MEMREGION_OVERLAP);

    memInfo             = cleanMemInfoReg0;
    memInfo.startIndex  = REGION2_START_IDX;
    validate ("Validate start index strictly increasing region 0",
              QMSS_MEMREGION_ORDERING);

    memInfo             = cleanMemInfoReg0;
    memInfo.descBase    = (uint32_t *)(REGION2_BASE(memBase));
    validate ("Validate base address strictly increasing for region 0",
              QMSS_MEMREGION_ORDERING);

    /* Now test errors regarding region 2 */
    memInfo             = cleanMemInfoReg2;
    memInfo.startIndex  = REGION0_START_IDX;
    validate ("Validate start index strictly increasing region 2",
              QMSS_MEMREGION_ORDERING);

    memInfo             = cleanMemInfoReg2;
    memInfo.descBase    = (uint32_t *)(REGION0_BASE(memBase));
    validate ("Validate base address strictly increasing for region 2",
              QMSS_MEMREGION_ORDERING);

    /* Now insert region 0 and 2 to make sure the configurations actually pass */
    memInfo             = cleanMemInfoReg0;
    validate ("Good configuration of region 0",
              QMSS_SOK);

    memInfo             = cleanMemInfoReg2;
    validate ("Good configuration of region 2",
              QMSS_SOK);

    System_printf("Total tests: %d; %d passed; %d failed\n", 
                  testCount, testCount - errorCount, errorCount);
}

Void run_test (Void)
{
    testInsertMemRegion ();
}


