/* ======================================================================
 *   Copyright (c) 2022 Texas Instruments Incorporated
 *
 *   All rights reserved. Property of Texas Instruments Incorporated.
 *   Restricted rights to use, duplicate or disclose this code are
 *   granted through contract.
 *
 *   The program may not be used without the written permission
 *   of Texas Instruments Incorporated or against the terms and conditions
 *   stipulated in the agreement under which this program has been
 *   supplied.
 * ==================================================================== */

/**
 *  \file     crc.c
 *
 *  \brief    This file contains crc APIs used by the MCAL apps
 *
 */

/* ========================================================================== */
/*                             Include Files                                  */
/* ========================================================================== */

#include "stdio.h"
#include "stdarg.h"
#include "stdlib.h"
#include "Std_Types.h"

#include "hal_stdtypes.h"
#include "hw_ctrl_core.h"
#include "sys_common.h"
#include "crc.h"


#define CRC_BASE               crcREG

static uint32 isCrcInitialized = 0;
/** @fn void crcInit(void)
*   @brief Initializes the crc Driver
*
*   This function initializes the crc module.
*/
uint32 crcInit(void)
{
    uint32 retVal = E_OK;
    uint32 regWrFailStatus, regVal;
    /******** CH1 (SPI) - 16 bits, Ch2 (MB/UART)- 32 bits *******/
    uint32 ctrl2Reg;

    /** @b initialize @b CRC */
    /** Power on CRC module */
    regWrFailStatus = regWriteReadback(&crcREG->CTRL1, M_THIRTY_ONE, M_ZERO, 0x0U);

    /** - Reset  PSA*/
    regWrFailStatus |= regWriteReadback(&crcREG->CTRL0, M_THIRTY_ONE, M_ZERO,  \
                                        ((uint32)((uint32)1U << 0U) | (uint32)((uint32)1U << 8U)));

    /** Setting Up Channel1:
     *       - 16 bits data size
     *       - CRC Type CRC-16
     *       - MSBit first
     *       - Byte swap Enable
     */
    regVal = (((uint32)((uint32)1U << 1U)    /* Data Size 16 Bit     */
           | (uint32)((uint32)1U << 3U)    /* CRC Type CRC-16      */
           | (uint32)((uint32)0U << 5U)    /* Bit Swap MSBit first */
           | (uint32)((uint32)1U << 6U)) >> M_ONE);   /* Byte Swap is Enable  */
    regWrFailStatus |= regWriteReadback(&crcREG->CTRL0, M_SIX, M_ONE, regVal);

    /** Setting Up Channel2:
     *       - 32 bits data size
     *       - CRC Type CRC32
     *       - LSBit first
     *       - Byte swap Disable
     */
    regVal = (((uint32)((uint32)2U << 9U)   /* Data Size 32 Bit     */
           | (uint32)((uint32)2U << 11)   /* CRC Type CRC-32      */
           | (uint32)((uint32)1U << 13U)  /* LSBit first          */
           | (uint32)((uint32)0U << 14U)) >> M_NINE); /* Byte Swap is Disable */
    regWrFailStatus |= regWriteReadback(&crcREG->CTRL0, M_FOURTEEN, M_NINE, regVal);

    /** - Setup the Data trace for channel1 */
    regWrFailStatus |= regWriteReadback(&crcREG->CTRL2, M_FOUR, M_FOUR, 0x0U);

    /** - Set interrupt enable
     *     - Enable/Disable timeout
     *     - Enable/Disable underrun interrupt
     *     - Enable/Disable overrun interrupt
     *     - Enable/Disable CRC fail interrupt
     *     - Enable/Disable compression interrupt
     */
    regVal = (uint32)(((uint32)0x0000001FU) | ((uint32)0x00001F00U));
    regWrFailStatus |= regWriteReadback(&crcREG->INTS, M_THIRTY_ONE, M_ZERO, regVal);

    /** - Setup pattern count preload register for channel 1 and channel 2*/
    regWrFailStatus |= regWriteReadback(&crcREG->PCOUNT_REG1, M_THIRTY_ONE, M_ZERO, 0x0U);
    regWrFailStatus |= regWriteReadback(&crcREG->PCOUNT_REG2, M_THIRTY_ONE, M_ZERO, 0x0U);

    /** - Setup sector count preload register for channel 1 and channel 2*/
    regWrFailStatus |= regWriteReadback(&crcREG->SCOUNT_REG1, M_THIRTY_ONE, M_ZERO, 0x0U);
    regWrFailStatus |= regWriteReadback(&crcREG->SCOUNT_REG2, M_THIRTY_ONE, M_ZERO, 0x0U);

    /** - Setup watchdog timeout for channel 1 and channel 2*/
    regWrFailStatus |= regWriteReadback(&crcREG->WDTOPLD1, M_THIRTY_ONE, M_ZERO, 0x00FFFFFFU);
    regWrFailStatus |= regWriteReadback(&crcREG->WDTOPLD2, M_THIRTY_ONE, M_ZERO, 0x00FFFFFFU);

    /** - Setup block complete timeout for channel 1 and channel 2*/
    regWrFailStatus |= regWriteReadback(&crcREG->BCTOPLD1, M_THIRTY_ONE, M_ZERO, 0x00FFFFFFU);
    regWrFailStatus |= regWriteReadback(&crcREG->BCTOPLD2, M_THIRTY_ONE, M_ZERO, 0x00FFFFFFU);

    /** - Setup CRC value low for channel 1 and channel 2*/
    regWrFailStatus |= regWriteReadback(&crcREG->REGL1, M_THIRTY_ONE, M_ZERO, 0x0U);
    regWrFailStatus |= regWriteReadback(&crcREG->REGL2, M_THIRTY_ONE, M_ZERO, 0x0U);

    /** - Setup CRC value high for channel 1 and channel 2*/
    regWrFailStatus |= regWriteReadback(&crcREG->REGH1, M_THIRTY_ONE, M_ZERO, 0x0U);
    regWrFailStatus |= regWriteReadback(&crcREG->REGH2, M_THIRTY_ONE, M_ZERO, 0x0U);

    /** - Setup the Channel mode */
    ctrl2Reg = crcREG->CTRL2;
    regVal = ctrl2Reg | (uint32)(CRC_FULL_CPU)  | (uint32)((uint32)CRC_FULL_CPU << 8U);
    regWrFailStatus |= regWriteReadback(&crcREG->CTRL2, M_THIRTY_ONE, M_ZERO, regVal);

    /** - Pulling Ch1 and CH2 PSA out of reset */
    regWrFailStatus |= regWriteReadback(&crcREG->CTRL0, M_ZERO, M_ZERO, 0x0U);
    regWrFailStatus |= regWriteReadback(&crcREG->CTRL0, M_EIGHT, M_EIGHT, 0x0U);

    /* Raise a fatal error if any of above register writes and comparison failed */
    if(M_ZERO != regWrFailStatus)
    {
        retVal = E_NOT_OK;
    }

    return (retVal);
}

/** @fn void crcChannelReset(crcBASE_t *crcBaseAddr, uint32 channel)
*   @brief Reset the channel configurations
*   @param[in] crcBaseAddr - crc module base address
*   @param[in] channel - crc channel
*                      CRC_CH1 - channel1
*                      CRC_CH2 - channel2
*                      CRC_CH3 - channel3
*                      CRC_CH4 - channel4
*
*   Reset configurations of the selected channels.
*/
uint32 crcChannelReset(crcBASE_t *crcBaseAddr, uint32 channel)
{
    uint32 retVal = E_OK;
    uint32 regWrFailStatus;

    if (channel == 0U)
    {
        /** Reset the CRC channel  */
        regWrFailStatus = regWriteReadback(&crcBaseAddr->CTRL0, M_ZERO, M_ZERO, 0x1U);
        /** Exit the reset  */
        regWrFailStatus |= regWriteReadback(&crcBaseAddr->CTRL0, M_ZERO, M_ZERO, 0x0U);
     }
     else if (channel == 1U)
     {
        /** Reset the CRC channel  */
        regWrFailStatus = regWriteReadback(&crcBaseAddr->CTRL0, M_EIGHT, M_EIGHT, 0x1U);
        /** Exit the reset  */
        regWrFailStatus |= regWriteReadback(&crcBaseAddr->CTRL0, M_EIGHT, M_EIGHT, 0x0U);
     }
     else
     {
         regWrFailStatus = 0U;
     }
    /* Raise a fatal error if any of above register writes and comparison failed */
    if(M_ZERO != regWrFailStatus)
    {
        retVal = E_NOT_OK;
    }

    return (retVal);
}

/** @fn void crcSetConfig(crcBASE_t *crcBaseAddr, crcConfig_t *param)
*   @brief Set crc configurations
*   @param[in] crcBaseAddr - crc module base address
*   @param[in] param - structure for channel configuration
*   Set Channel parameters
*/
uint32 crcSetConfig(const crcBASE_t *crcBaseAddr, const crcConfig_t *param)
{
    uint32 retVal = E_OK;
    uint32 regWrFailStatus, regVal;

    regWrFailStatus = 0U;
    switch (param->crcChanNum)
    {
        /** Setting CRC Channel 0 settings */
        case 0U:
            regVal = (((uint32)((uint32)param->crcDataSize << 1U)     /* Data Size   */
                 | (uint32)((uint32)(((uint32)param->crcType) & 3U) << 3U)/* CRC Type 16/32/64*/
                 | (uint32)((uint32)param->crcBitSwap << 5U) /* Bit Swap MSBit/LSBit first */
                 | (uint32)((uint32)param->crcByteSwap << 6U) /* Byte Swap enable/disable  */
                 | (uint32)((uint32)(((uint32)param->crcType) & 4U) << (7U - 2U))) >> M_ONE);
            regWrFailStatus |= regWriteReadback((volatile uint32*)&crcBaseAddr->CTRL0, \
                                                                  M_SIX, M_ONE, regVal);
            regWrFailStatus |= regWriteReadback((volatile uint32*)&crcBaseAddr->PCOUNT_REG1, \
                                                                   M_NINETEEN, M_ZERO, \
                                                                   param->pcount);
            regWrFailStatus |= regWriteReadback((volatile uint32*)&crcBaseAddr->SCOUNT_REG1, \
                                                                  M_FIFTEEN, M_ZERO, \
                                                                  param->scount);
            regWrFailStatus |= regWriteReadback((volatile uint32*)&crcBaseAddr->WDTOPLD1, \
                                                                  M_TWENTY_THREE, M_ZERO, \
                                                                  param->wdg_preload);
            regWrFailStatus |= regWriteReadback((volatile uint32*)&crcBaseAddr->BCTOPLD1, \
                                                                   M_TWENTY_THREE, M_ZERO, \
                                                                   param->block_preload);
            regWrFailStatus |= regWriteReadback((volatile uint32*)&crcBaseAddr->CTRL2, \
                                                                  M_ONE, M_ZERO, \
                                                                  param->crcChanMode);

            regWrFailStatus |= regWriteReadback((volatile uint32*)&crcBaseAddr->CTRL0, \
                                                                  M_ZERO, M_ZERO, 0x1U);
            regWrFailStatus |= regWriteReadback((volatile uint32*)&crcBaseAddr->CTRL0, \
                                                                  M_ZERO, M_ZERO, 0x0U);
            break;
        case 1U:
            regVal = (((uint32)((uint32)param->crcDataSize << 9U)  /* Data Size   */
                  | (uint32)((uint32)(((uint32)param->crcType) & 3U) << 11U)/* CRC Type  16/32/64*/
                  | (uint32)((uint32)param->crcBitSwap << 13U)  /* Bit Swap MSBit/LSBit first */
                  | (uint32)((uint32)param->crcByteSwap << 14U) /* Byte Swap is Enable/Disable*/
                  | (uint32)((uint32)(((uint32)param->crcType) & 4U) << (15U - 2U))) >> M_NINE);
            regWrFailStatus |= regWriteReadback((volatile uint32*)&crcBaseAddr->CTRL0, \
                                                                  M_FOURTEEN, M_NINE, regVal);
            regWrFailStatus |= regWriteReadback((volatile uint32*)&crcBaseAddr->PCOUNT_REG2, \
                                                                  M_NINETEEN, M_ZERO, \
                                                                  param->pcount);
            regWrFailStatus |= regWriteReadback((volatile uint32*)&crcBaseAddr->SCOUNT_REG2, \
                                                                  M_FIFTEEN, M_ZERO, \
                                                                  param->scount);
            regWrFailStatus |= regWriteReadback((volatile uint32*)&crcBaseAddr->WDTOPLD2, \
                                                                  M_TWENTY_THREE, M_ZERO, \
                                                                  param->wdg_preload);
            regWrFailStatus |= regWriteReadback((volatile uint32*)&crcBaseAddr->BCTOPLD2, \
                                                                  M_TWENTY_THREE, M_ZERO, \
                                                                  param->block_preload);
            regWrFailStatus |= regWriteReadback((volatile uint32*)&crcBaseAddr->CTRL2, \
                                                                  M_NINE, M_EIGHT, \
                                                                  param->crcChanMode);

            regWrFailStatus |= regWriteReadback((volatile uint32*)&crcBaseAddr->CTRL0, \
                                                                  M_EIGHT, M_EIGHT, 0x1U);
            regWrFailStatus |= regWriteReadback((volatile uint32*)&crcBaseAddr->CTRL0, \
                                                                  M_EIGHT, M_EIGHT, 0x0U);
            break;
        default :
        /*Do Nothing*/
            break;
    }

    /* Raise a fatal error if any of above register writes and comparison failed */
    if(M_ZERO != regWrFailStatus)
    {
        retVal = E_NOT_OK;
    }

    return (retVal);
}

/** @fn void crcSignGen(crcBASE_t *crcBaseAddr, crcModConfig_t *param)
*   @brief set the mode specific parameters for signature generation
*   @param[in] crcBaseAddr - crc module base address
*   @param[in] param - structure holding mode specific parameters
*   Generate CRC signature
*/
void crcSignGen(const crcBASE_t *crcBaseAddr, const crcModConfig_t *param)
{
    uint32 loopCnt;
    volatile uint64 *psaSign, *ptr64;
    volatile uint32 *ptr32, *psaSigx_ptr32;
    volatile uint16 *ptr16, *psaSigx_ptr16;

    switch (param->crcDataSize)
    {
        case CRC_DSIZE_HWORD:
            /*AR_CODE_REVIEW MR:R.11.1,R.11.2,R.11.4 <INSPECTED> "Conversion required to calculate
             * CRC" */
            /*LDRA_INSPECTED 94 S */
            /*LDRA_INSPECTED 95 S */
            ptr16 = (uint16*)param->src_data_pat;
            /*AR_CODE_REVIEW MR:R.11.2,4,6,7 <INSPECTED> "conversion needed" */
            /*LDRA_INSPECTED 439 S */
            /*AR_CODE_REVIEW MR:R.11.1,2,4,6,7 <INSPECTED> "conversion needed" */
            /*LDRA_INSPECTED 440 S */
            psaSigx_ptr16 = (uint16 *)((uint32)(&crcBaseAddr->PSA_SIGREGL1) + \
                                      (((uint32)(param->crcChSel)) * ((uint32)0x40U)));
            for (loopCnt=0U; loopCnt < param->crcDataLen; loopCnt++)
            {
                /*AR_CODE_REVIEW MR:R.21.1 <INSPECTED> "Valid non NULL input parameters are only
                * allowed in this driver" */
                /*LDRA_INSPECTED 45 D */
                *psaSigx_ptr16 = *ptr16;
                /*AR_CODE_REVIEW MR:R.17.1,R.17.4 <INSPECTED> "Pointer increment is essential
                 * to get the next data" */
                /*LDRA_INSPECTED 567 S */
                ptr16++;
            }
            break;

        case CRC_DSIZE_WORD:
            /*AR_CODE_REVIEW MR:R.11.1,R.11.2,R.11.4 <INSPECTED> "Conversion required to calculate
             * CRC" */
            /*LDRA_INSPECTED 94 S */
            /*LDRA_INSPECTED 95 S */
            ptr32 = (uint32*)param->src_data_pat;
            /*AR_CODE_REVIEW MR:R.11.2,4,6,7 <INSPECTED> "conversion needed" */
            /*LDRA_INSPECTED 439 S */
            /*AR_CODE_REVIEW MR:R.11.1,2,4,6,7 <INSPECTED> "conversion needed" */
            /*LDRA_INSPECTED 440 S */
            psaSigx_ptr32 = (uint32 *)((uint32)(&crcBaseAddr->PSA_SIGREGL1) + \
                                      (((uint32)(param->crcChSel)) * ((uint32)0x40U)));
            for (loopCnt = 0U; loopCnt < param->crcDataLen; loopCnt++)
            {
                /*AR_CODE_REVIEW MR:R.21.1 <INSPECTED> "Valid non NULL input parameters are only
                * allowed in this driver" */
                /*LDRA_INSPECTED 45 D */
                *psaSigx_ptr32 = *ptr32;
                /*AR_CODE_REVIEW MR:R.17.1,R.17.4 <INSPECTED> "Pointer increment is essential
                 * to get the next data" */
                /*LDRA_INSPECTED 567 S */
                ptr32++;
            }
            break;
        case CRC_DSIZE_DWORD:
            ptr64 = (uint64 *)param->src_data_pat;
            /*AR_CODE_REVIEW MR:R.11.2,4,6,7 <INSPECTED> "conversion needed" */
            /*LDRA_INSPECTED 439 S */
            /*AR_CODE_REVIEW MR:R.11.1,2,4,6,7 <INSPECTED> "conversion needed" */
            /*LDRA_INSPECTED 440 S */
            psaSign = (uint64 *)((uint32)(&crcBaseAddr->PSA_SIGREGL1) + \
                                (((uint32)(param->crcChSel)) * ((uint32)0x40U)));
            for (loopCnt = 0U; loopCnt < param->crcDataLen; loopCnt++)
            {
                /*AR_CODE_REVIEW MR:R.21.1 <INSPECTED> "Valid non NULL input parameters are only
                * allowed in this driver" */
                /*LDRA_INSPECTED 45 D */
                *psaSign = *ptr64;
                /*AR_CODE_REVIEW MR:R.17.1,R.17.4 <INSPECTED> "Pointer increment is
                 * essential to get the next data" */
                /*LDRA_INSPECTED 567 S */
                ptr64++;
            }
            break;
        default:
              /*do nothing*/
            break;
    }

}

/** @fn uint32 crcGetPSASig(crcBASE_t *crcBaseAddr, uint32 channel, uint8 crcTypeIn)
*   @brief get genearted PSA signature used for FULL CPU mode
*   @param[in] crcBaseAddr - crc module base address
*   @param[in] channel - crc channel
*                      CRC_CH1 - channel1
*                      CRC_CH2 - channel2
*                      CRC_CH3 - channel3
*                      CRC_CH4 - channel4
*   @param[in] crcTypeIn - CRC Type CRC_TYPE_CRC64/CRC_TYPE_CRC16/CRC_TYPE_CRC32
*   @return uint64 PSA signature
*   Get PSA signature used for FULL CPU mode of selected channel
*/
uint64 crcGetPSASig(const crcBASE_t *crcBaseAddr, uint32 channel, uint8 crcTypeIn)
{
    uint64 status;
    uint64 CRC_PSA_SIGREGH1 = (uint64)crcBaseAddr->PSA_SIGREGH1;
    uint64 CRC_PSA_SIGREGL1 = (uint64)crcBaseAddr->PSA_SIGREGL1;
    uint64 CRC_PSA_SIGREGH2 = (uint64)crcBaseAddr->PSA_SIGREGH2;
    uint64 CRC_PSA_SIGREGL2 = (uint64)crcBaseAddr->PSA_SIGREGL2;

    switch (channel)
    {
        case 0U:
            if (crcTypeIn == CRC_TYPE_CRC64)
            {
                status = (CRC_PSA_SIGREGH1 << (uint64)32U) | (CRC_PSA_SIGREGL1);
            }
            else
            {
                status = (CRC_PSA_SIGREGL1);
            }

            break;
        case 1U:
            if (crcTypeIn == CRC_TYPE_CRC64)
            {
                status = (CRC_PSA_SIGREGH2 << (uint64)32U) | (CRC_PSA_SIGREGL2);

            }
            else
            {
                status = (CRC_PSA_SIGREGL2);
            }
            break;
        default :
            status = 0U;
            break;
    }
    return status;

}

uint16 crcCompute(uint8 *inData, uint32 inDataLen)
{
    uint32 retVal = E_OK;
    crcConfig_t crcConfigVal;
    crcModConfig_t crcModConf;
    uint64 crcCalculated = 0;
    uint16 crcVal = 0;

    if(isCrcInitialized == 0)
    {
        retVal = crcInit();
        if(E_OK == retVal)
        {
            isCrcInitialized = 1;
        }
    }

    if(E_OK == retVal)
    {
        retVal = crcChannelReset(CRC_BASE, CRC_CH1);
    }

    if(E_OK == retVal)
    {
        /* Set the CRC channel to CH1 */
        crcConfigVal.crcChanNum = CRC_CH1;
        /* Set the Data Size to HALF WORD size */
        crcConfigVal.crcDataSize = CRC_DSIZE_HWORD;
        /* Set the CRC Type to CRC-16 */
        crcConfigVal.crcType = CRC_TYPE_CRC16;
        /* Set CRC Bit Swap to MSB First */
        crcConfigVal.crcBitSwap = CRC_MSBIT_FIRST;
        /* Disable CRC Byte Swap */
        crcConfigVal.crcByteSwap = CRC_BYTE_SWAP_ENA;
        /* Set CRC Channel Mode to CPU mode */
        crcConfigVal.crcChanMode = CRC_CHMODE_CPU;
        crcConfigVal.pcount = inDataLen;
        crcConfigVal.scount = ((uint32)1U);
        crcConfigVal.wdg_preload = ((uint32)0x00FFFFFFU);
        crcConfigVal.block_preload = ((uint32)0x00FFFFFFU);

        retVal = crcSetConfig(CRC_BASE, &crcConfigVal);
    }

    if(E_OK == retVal)
    {
        crcModConf.crcChSel = CRC_CH1;
        crcModConf.crcDataLen = inDataLen/2;
        crcModConf.mode = CRC_FULL_CPU;
        /*AR_CODE_REVIEW MR:R.11.1,2,3,5,7 <INSPECTED> "conversion needed" */
        /*LDRA_INSPECTED 94 S */ /*LDRA_INSPECTED 95 S */
        crcModConf.src_data_pat = (uint64*)inData;
        crcModConf.crcDataSize = CRC_DSIZE_HWORD;
        crcModConf.crcType = 0U;
        crcSignGen(CRC_BASE, &crcModConf);

        /* Get the computed CRC value */
        crcCalculated = (uint64)(crcGetPSASig(CRC_BASE, CRC_CH1, CRC_TYPE_CRC16) & 0xFFFFU);
    }

    crcVal = (uint16)crcCalculated;

    return (crcVal);
}
