/**
 *******************************************************************************
 * @file    dpm32m0xx_W.h
 *
 * @brief   Source file for CRC firmware driver.
 *          This file provides all the CRC firmware functions
 *          functionalities of the Cyclic Redundancy Check(CRC) peripheral:
 *           + Initialization functions.
 *           + Configuration functions.
 *
 * @author  DPM
 *
 * @version V1.0.0
 *
 * @date    2023-11-01
 *
 * @verbatim
 ===============================================================================
                       ##### How to use this driver #####
 ===============================================================================
    [..]
      (#) Enable The CRC controller clock using
          RCC_APBPeriphClockCmd(RCC_APB_PERIPH_CRC, ENABLE)

      (#) Configure CRC module: Initialize the CRC module to the required
          operating mode and parameters using the CRC_Init() function :
        (+) Configure input data bit width using the CRC_SetMode() function.
            Set the input data bit width of the CRC module based on the bit
            width of the input data, which can be 8 bits, 16 bits, or 32 bits.
        (+) Set CRC input data using the CRC_SetInitValue() function.
        (+) Set input value inversion using the CRC_SetInputReverseType()
            function.
        (+) Set output value inversion output using the
            CRC_SetOutputReverseType() function.
        (+) Set output value XOR output using the CRC_SetOutputXorType()
            function.

      (#) Read CRC verification results using the CRC_GetCRCResult() function :
        (+) In case of Single number calculation(e.g. 16bit) using the
            CRC_CalcCRC16bits() function.
        (+) In case of a continuous data stream calculation(e.g. 16bit) using
            CRC_CalcCRCBuf16bits() function.

 * @endverbatim
 *******************************************************************************/
#include "dpm32m0xx_crc.h"


/**
 *******************************************************************************
 * @brief   Deinitializes CRC peripheral registers to their default reset values.
 * @retval  None.
********************************************************************************/
void CRC_DeInit(void)
{
  /* Set CRC control register to reset value. */
  CRC->CR = 0x00000000UL;

  /* Set CRC data register to reset value. */
  CRC->DATA = 0x00000000UL;

  /* Set CRC initial value register to reset value. */
  CRC->INIT = 0xFFFFFFFFUL;
}

/**
 *******************************************************************************
 * @brief   Initializes the CRC peripheral according to the specified parameters
 *          in the CRC_InitType.
 * @param   [in]  CRC_InitType: Structure pointer of CRC configuration.
 * @retval  None.
********************************************************************************/
void CRC_Init(CRC_InitTypeStruct *CRC_InitType)
{
  uint32_t tmpReg = 0UL;

  /* Parameters check. */
  PARAM_ASSERT(IS_CRC_MODE(CRC_InitType->CRC_Mode));
  PARAM_ASSERT(IS_CRC_INPUT_REV_TYPE(CRC_InitType->CRC_InputRevType));
  PARAM_ASSERT(IS_CRC_OUTPUT_REV_TYPE(CRC_InitType->CRC_OutputRevType));
  PARAM_ASSERT(IS_CRC_OUTPUT_XOR_TYPE(CRC_InitType->CRC_OutputXorType));

  /* Read the value of the CRC control register. */
  tmpReg = CRC->CR;

  /* Clear rcr_mode, rec_in, rec_out, xor_out bit. */
  tmpReg &= ~(uint32_t)(CRC_CR_MODE_Msk | CRC_CR_REV_IN_Msk | CRC_CR_REV_OUT_Msk \
                        | CRC_CR_XOR_OUT_Msk);

  /* Set CRC mode bit according to CRC_InitType. */
  tmpReg |= (uint32_t)(CRC_InitType->CRC_Mode);

  /* Set CRC rev_in bit according to CRC_InitType. */
  tmpReg |= (uint32_t)(CRC_InitType->CRC_InputRevType);

  /* Set CRC rev_out bit according to CRC_InitType. */
  tmpReg |= (uint32_t)(CRC_InitType->CRC_OutputRevType);

  /* Set CRC xor_out bit according to CRC_InitType. */
  tmpReg |= (uint32_t)(CRC_InitType->CRC_OutputXorType);

  /* Set CR value*/
  CRC->CR = tmpReg;

  /* Set the value of the INIT register.*/
  CRC->INIT = CRC_InitType->CRC_InitValue;
}

/**
 *******************************************************************************
 * @brief   Initialize the CRC_InitType with default parameters.
 * @param   [in]  CRC_InitType: Pointer to a CRC_InitTypeStruct structure
 *                which will be initialized.
 * @retval  None.
 ******************************************************************************/
void CRC_StructInit(CRC_InitTypeStruct *CRC_InitType)
{
  CRC_InitType->CRC_Mode = CRC_MODE_CRC32;
  CRC_InitType->CRC_InputRevType = CRC_INPUT_REV_NO;
  CRC_InitType->CRC_OutputRevType = CRC_OUTPUT_REV_NO;
  CRC_InitType->CRC_OutputXorType = CRC_OUTPUT_XOR_ALL0;
  CRC_InitType->CRC_InitValue = 0x00000000UL;
}

/**
 *******************************************************************************
 * @brief   Set CRC calculate mode.
 * @param   [in]  CRC_Mode: Specifies the CRC mode.
 *                This parameter can be a value of @ref CRC_ModeEnum.
 *                  @arg CRC_MODE_CRC32: CRC32.
 *                  @arg CRC_MODE_CRC16_1021: CRC16_1021.
 *                  @arg CRC_MODE_CRC16_8005: CRC_MODE_CRC16_8005.
 * @retval  None.
********************************************************************************/
void CRC_SetMode(CRC_ModeEnum CRC_Mode)
{
  uint32_t tmpReg = 0UL;

  /* Get CRC configuration register value. */
  tmpReg = CRC->CR;

  /* Clear CR_MODE[1:0] bits. */
  tmpReg &= ~((uint32_t)CRC_CR_MODE_Msk);

  /* Set CR_MODE[1:0] bits. */
  tmpReg |= (uint32_t)(CRC_Mode);

  /* Set CRC configuration register value. */
  CRC->CR = tmpReg;
}

/**
 *******************************************************************************
 * @brief   Set CRC input data reverse type.
 * @param   [in]  CRC_InputRevType: Specifies the CRC input data type.
 *                This parameter can be a value of @ref CRC_InputRevTypeEnum.
 *                  @arg CRC_INPUT_REV_NO: Input data not reverse.
 *                  @arg CRC_INPUT_REV_8BITS: Input data reverse at 8 bits.
 *                  @arg CRC_INPUT_REV_16BITS: Input data reverse at 16 bits.
 *                  @arg CRC_INPUT_REV_32BITS: Input data reverse at 32 bits.
 * @retval  None.
********************************************************************************/
void CRC_SetInputReverseType(CRC_InputRevTypeEnum CRC_InputRevType)
{
  uint32_t tmpReg = 0UL;

  /* Get CRC configuration register value. */
  tmpReg = CRC->CR;

  /* Clear REV_IN[1:0] bits. */
  tmpReg &= (uint32_t)~((uint32_t)CRC_CR_REV_IN_Msk);

  /* Set REV_IN[1:0] bits. */
  tmpReg |= (uint32_t)(CRC_InputRevType);

  /* Set CRC configuration register value. */
  CRC->CR = (uint32_t)tmpReg;
}

/**
 *******************************************************************************
 * @brief   Set CRC output data reverse type.
 * @param   [in]  CRC_OutputRevType: Specifies the CRC output data reverse type.
 *                This parameter can be a value of @ref CRC_InputRevTypeEnum.
 *                  @arg CRC_OUTPUT_REV_NO: Output data not reverse.
 *                  @arg CRC_OUTPUT_REV_YES: Output data reverse.
 * @retval  None.
********************************************************************************/
void CRC_SetOutputReverseType(CRC_OutputRevTypeEnum CRC_OutputRevType)
{
  uint32_t tmpReg = 0UL;

  /* Get CRC configuration register value. */
  tmpReg = CRC->CR;

  /* Clear REV_OUT bits. */
  tmpReg &= (uint32_t)~((uint32_t)CRC_CR_REV_OUT_Msk);

  /* Set REV_OUT bits. */
  tmpReg |= (uint32_t)(CRC_OutputRevType);

  /* Set CRC configuration register value. */
  CRC->CR = (uint32_t)tmpReg;
}

/**
 *******************************************************************************
 * @brief   Set CRC output data xor type.
 * @param   [in]  CRC_OutputXorType: Specifies the CRC output data xor type.
 *                This parameter can be a value of @ref CRC_InputRevTypeEnum.
 *                  @arg CRC_OUTPUT_XOR_ALL0: Output data xor 0x00000000.
 *                  @arg CRC_OUTPUT_XOR_ALLF: Output data xor 0xFFFFFFFF.
 * @retval  None.
********************************************************************************/
void CRC_SetOutputXorType(CRC_OutputXorTypeEnum CRC_OutputXorType)
{
  uint32_t tmpReg = 0UL;

  /* Get CR register value. */
  tmpReg = CRC->CR;

  /* Reset XOR_OUT bit. */
  tmpReg &= (uint32_t)~((uint32_t)CRC_CR_XOR_OUT_Msk);

  /* Set XOR_OUT bit. */
  tmpReg |= (uint32_t)(CRC_OutputXorType);

  /* Set CRC configuration register value. */
  CRC->CR = (uint32_t)tmpReg;
}

/**
 *******************************************************************************
 * @brief   Set CRC initial value regisiter.
 * @param   [in]  CRC_InitValue: CRC initial value.
 * @retval  None.
********************************************************************************/
void CRC_SetInitValue(uint32_t CRC_InitValue)
{
  CRC->INIT = CRC_InitValue;
}

/**
 *******************************************************************************
 * @brief   Calculate 8 bits data CRC value.
 * @param   [in]  CRC_Data: Calculate data.
 * @retval  uint32_t: Calculate result.
********************************************************************************/
#if defined (DPM32M036) || defined (DPM32M030) || defined (DPM32M015) || \
    defined (DPM32M026) || defined (DPM32T55x)
uint32_t CRC_CalcCRC8bits(uint8_t CRC_Data)
#else /* DPM32M08x || DPM32M05x || DPM32M03x */
void CRC_CalcCRC8bits(uint8_t CRC_Data)
#endif /* DPM32M036 || DPM32M030 || DPM32M015 || DPM32M026 || DPM32T55x */
{
  *(__IO uint8_t *)(__IO void *)(&CRC->DATA) = CRC_Data;
#if defined (DPM32M036) || defined (DPM32M030) || defined (DPM32M015) || \
    defined (DPM32M026) || defined (DPM32T55x)
  /* Return the CRC computed value */
  return CRC->RESULT;
#endif /* DPM32M036 || DPM32M030 || DPM32M015 || DPM32M026 || DPM32T55x */
}

/**
 *******************************************************************************
 * @brief   Calculate 16 bits data CRC value.
 * @param   [in]  CRC_Data: Calculate data.
 * @retval  uint32_t: Calculate result.
********************************************************************************/
#if defined (DPM32M036) || defined (DPM32M030) || defined (DPM32M015) || \
    defined (DPM32M026) || defined (DPM32T55x)
uint32_t CRC_CalcCRC16bits(uint16_t CRC_Data)
#else /* DPM32M08x || DPM32M05x || DPM32M03x */
uint16_t CRC_CalcCRC16bits(uint16_t CRC_Data)
#endif /* DPM32M036 || DPM32M030 || DPM32M015 || DPM32M026 || DPM32T55x */
{
  *(__IO uint16_t *)(__IO void *)(&CRC->DATA) = CRC_Data;

  /* Return the CRC computed value */
#if defined (DPM32M036) || defined (DPM32M030) || defined (DPM32M015) || \
    defined (DPM32M026) || defined (DPM32T55x)
  return CRC->RESULT;
#else /* DPM32M08x || DPM32M05x || DPM32M03x */
  return (uint16_t)(CRC->RESULT);
#endif /* DPM32M036 || DPM32M030 || DPM32M015 || DPM32M026 || DPM32T55x */
}

/**
 *******************************************************************************
 * @brief   Calculate 32 bits data CRC value.
 * @param   [in]  CRC_Data: Calculate data.
 * @retval  uint32_t: Calculate result.
********************************************************************************/
uint32_t CRC_CalcCRC32bits(uint32_t CRC_Data)
{
  *(__IO uint32_t *)(__IO void *)(&CRC->DATA) = CRC_Data;

  /* Return the CRC computed value */
  return CRC->RESULT;
}

/**
 *******************************************************************************
 * @brief   Calculate buffer CRC with 8 bit length.
 * @param   [in]  pBuffer: Calculate buffer.
 * @param   [in]  BufferLength: Calculate buffer length.
 * @retval  uint32_t: Calculate result.
********************************************************************************/
uint32_t CRC_CalcCRCBuf8bits(uint8_t* pBuffer, uint32_t BufferLength)
{
  uint32_t i = 0; /* input data buffer index */
  uint16_t data = 0;
  __IO uint16_t *pReg = NULL;

  for(i = 0U; i < (BufferLength / 4U); i++)
  {
    CRC->DATA = (((uint32_t)pBuffer[4U * i] << 24U) | \
                 ((uint32_t)pBuffer[(4U * i) + 1U] << 16U) | \
                 ((uint32_t)pBuffer[(4U * i) + 2U] << 8U)  | \
                 (uint32_t)pBuffer[(4U * i) + 3U]);
  }

  /* last bytes specific handling */
  if((BufferLength % 4U) != 0U)
  {
    if((BufferLength % 4U) == 1U)
    {
      *(__IO uint8_t *)(__IO void *)(&CRC->DATA) = pBuffer[4U * i];
    }

    if((BufferLength % 4U) == 2U)
    {
      data = ((uint16_t)((pBuffer[4U * i]) << 8U) | (uint16_t)pBuffer[(4U * i) + 1U]);
      pReg = (__IO uint16_t *)(__IO void *)(&CRC->DATA);
      *pReg = data;
    }

    if((BufferLength % 4U) == 3U)
    {
      data = ((uint16_t)((pBuffer[4U * i]) << 8U) | (uint16_t)pBuffer[(4U * i) + 1U]);
      pReg = (__IO uint16_t *)(__IO void *)(&CRC->DATA);
      *pReg = data;

      *(__IO uint8_t *)(__IO void *)(&CRC->DATA) = pBuffer[(4U * i) + 2U];
    }
  }

  /* Return the CRC computed value */
  return CRC->RESULT;
}

/**
 *******************************************************************************
 * @brief   Calculate buffer CRC with 16 bit length.
 * @param   [in]  pBuffer: Calculate buffer.
 * @param   [in]  BufferLength: Calculate buffer length.
 * @retval  uint32_t: Calculate result.
********************************************************************************/
uint32_t CRC_CalcCRCBuf16bits(uint16_t* pBuffer, uint32_t BufferLength)
{
  uint32_t i = 0;  /* input data buffer index */
  __IO uint16_t *pReg = NULL;

  for(i = 0U; i < (BufferLength / 2U); i++)
  {
    CRC->DATA = ((uint32_t)pBuffer[2U * i] << 16U) | (uint32_t)pBuffer[(2U * i) + 1U];
  }

  if((BufferLength % 2U) != 0U)
  {
    pReg = (__IO uint16_t *)(__IO void *)(&CRC->DATA);
    *pReg = pBuffer[2U * i];
  }

  /* Return the CRC computed value */
  return CRC->RESULT;
}

/**
 *******************************************************************************
 * @brief   Calculate buffer CRC with 32 bit length.
 * @param   [in]  pBuffer: Calculate buffer.
 * @param   [in]  BufferLength: Calculate buffer length.
 * @retval  uint32_t: Calculate result.
********************************************************************************/
uint32_t CRC_CalcCRCBuf32bits(uint32_t* pBuffer, uint32_t BufferLength)
{
  uint32_t i = 0;

  for(i = 0; i < BufferLength; i++)
  {
    CRC->DATA = pBuffer[i];
  }

  /* Return the CRC computed value */
  return CRC->RESULT;
}

/**
 *******************************************************************************
 * @brief   Get CRC calculate result.
 * @retval  uint32_t: Calculate result.
********************************************************************************/
uint32_t CRC_GetCRCResult(void)
{
  /* Return the CRC computed value */
  return CRC->RESULT;
}

