/**
 *******************************************************************************
 * @file    dpm32m0xx_dma.c
 *
 * @brief   Source file for DMA firmware driver.
 *          This file provides firmware functions to manage the following :
 *          functionalities of the Direct Memory Access controller (DMA)
 *          peripheral :
 *           + Initialization and Configuration
 *           + Software trigger
 *           + Data Counter and data round
 *           + Interrupts and flags management
 *
 * @author  DPM
 *
 * @version V1.0.0
 *
 * @date    2023-11-01
 *
 * @verbatim
 ===============================================================================
                       ##### How to use this driver #####
 ===============================================================================
    [..]
      (#) Enable The DMA controller clock using
          RCC_APBPeriphClockCmd(RCC_APB_PERIPH_DMA, ENABLE).

      (#) Enable and configure the peripheral to be connected to the DMA Stream.

      (#) For a given Stream, program the required configuration through
          following parameters:
          Source and Destination addresses, Transfer Direction, Transfer size,
          Source and Destination data formats, Circular or Normal mode, Source
          and Destination Incrementation mode, FIFO mode and its Threshold
          (if needed) using the DMA_Init() function.
          To avoid filling unnecessary fields, you can call DMA_StructInit()
          function to initialize a given structure with default values (reset
          values), the modifyonly necessary fields.
          (ie. Source and Destination addresses, Transfer size and Data Formats).

      (#) Enable the NVIC and the corresponding interrupt(s) using the function
          DMA_IntCmd() if you need to use DMA interrupts.

      (#) Enable the DMA stream using the DMA_Cmd() function.

      (#) Activate the needed Stream Request using PPP_DMACmd() function for
          any PPP peripheral. The function allowing this operation is provided
          in each PPP peripheral driver (ie. SPI_DMACmd for SPI peripheral).
          Once the Stream is enabled, it is not possible to modify its
          configuration unless the stream is stopped and disabled.
          After enabling the Stream, it is advised to monitor the status bits
          using the function DMA_GetTransmitStatus().

      (#) To control DMA events you can use one of the following two methods:
        (+) Check on DMA Stream flags using the function DMA_GetTransmitStatus().
        (+) Use DMA interrupts through the function DMA_IntCmd() at
            initialization phase and DMA_GetIntFlagStatus() function into
            interrupt routines in communication phase.After checking on a flag
            you should clear it using DMA_ClearIntFlag()function.

 * @endverbatim
 *******************************************************************************/

#include "dpm32m0xx_dma.h"

#if defined (DPM32M08x) || defined (DPM32M05x)

/**
 *******************************************************************************
 * @brief   Deinitializes the DMA peripheral registers to their default reset values.
 * @param   [in]  DMA_Channel: Specifies the DMA channel.
 *                This parameter can be a value of @ref DMA_ChannelEnum.
 *                  @arg DMA_CH0: DMA channel 0.
 *                  @arg DMA_CH1: DMA channel 1.
 *                  @arg DMA_CH2: DMA channel 2.
 *                  @arg DMA_CH3: DMA channel 3.
 *                  @arg DMA_CH4: DMA channel 4.
 * @retval  None.
 ******************************************************************************/
void DMA_DeInit(DMA_ChannelEnum DMA_Channel)
{
  /* Parameters check. */
  PARAM_ASSERT(IS_DMA_CHANNEL(DMA_Channel));

  /* Reset DMA channel configuration 0 register. */
  DMA_CH_CFG0(DMA_Channel) = (uint32_t)0x00000000u;
  /* Reset DMA channel configuration 1 register. */
  DMA_CH_CFG1(DMA_Channel) = (uint32_t)0x00000000u;
  /* Reset DMA channel source address register. */
  DMA_CH_SRC_ADDR(DMA_Channel) = (uint32_t)0x00000000u;
  /* Reset DMA channel destination address register. */
  DMA_CH_DST_ADDR(DMA_Channel) = (uint32_t)0x00000000u;
  /* Reset DMA channel status register. */
  DMA_SR() = (uint32_t)(0x03u << (DMA_Channel * 4u));
}

/**
 *******************************************************************************
 * @brief   Initializes the DMA peripheral according to the specified parameters
 *          in the DMA_InitType.
 * @param   [in]  DMA_Channel: Specifies the DMA channel.
 *                This parameter can be a value of @ref DMA_ChannelEnum.
 *                  @arg DMA_CH0: DMA channel 0.
 *                  @arg DMA_CH1: DMA channel 1.
 *                  @arg DMA_CH2: DMA channel 2.
 *                  @arg DMA_CH3: DMA channel 3.
 *                  @arg DMA_CH4: DMA channel 4.
 * @param   [in]  DMA_InitType: Pointer to a DMA_InitTypeStruct structure
 *                which will be initialized.
 * @retval  None.
 ******************************************************************************/
void DMA_Init(DMA_ChannelEnum DMA_Channel, DMA_InitTypeStruct* DMA_InitType)
{
  uint32_t tmpReg0 = 0u, tmpReg1 = 0u;

  /* Parameters check. */
  PARAM_ASSERT(IS_DMA_CHANNEL(DMA_Channel));
  PARAM_ASSERT(IS_DMA_REQ_SOURCE(DMA_InitType->DMA_ReqSource));
  PARAM_ASSERT(IS_DMA_DATA_SIZE(DMA_InitType->DMA_DataSize));
  PARAM_ASSERT(IS_DMA_ADDR_MODE(DMA_InitType->DMA_SrcIncMode));
  PARAM_ASSERT(IS_DMA_ADDR_MODE(DMA_InitType->DMA_DstIncMode));
  PARAM_ASSERT(IS_FUNCTION_STATE(DMA_InitType->DMA_ErrIntState));
  PARAM_ASSERT(IS_FUNCTION_STATE(DMA_InitType->DMA_FISIntState));
  PARAM_ASSERT(IS_FUNCTION_STATE(DMA_InitType->DMA_CICRState));

  /* Read the value of the DMA channel config0 register. */
  tmpReg0 = DMA_CH_CFG0(DMA_Channel);

  /* Clear DMA CIRC FIS_IE ERR_IE REQ_SEL bits. */
  tmpReg0 &= ~(DMA_CH0_CFG0_CIRC_Msk | DMA_CH0_CFG0_FIS_IE_Msk \
               | DMA_CH0_CFG0_ERR_IE_Msk | DMA_CH0_CFG0_REQ_SEL_Msk);

  /* Set DMA CIRC bit according to DMA_InitType. */
  tmpReg0 |= (uint32_t)(DMA_InitType->DMA_CICRState << DMA_CH0_CFG0_CIRC_Pos);

  /* Set DMA FIS_IE bit according to DMA_InitType. */
  tmpReg0 |= (uint32_t)(DMA_InitType->DMA_FISIntState << DMA_CH0_CFG0_FIS_IE_Pos);

  /* Set DMA ERR_IE bit according to DMA_InitType. */
  tmpReg0 |= (uint32_t)(DMA_InitType->DMA_ErrIntState << DMA_CH0_CFG0_ERR_IE_Pos);

  /* Set DMA REQ_SEL[4:0] bits according to DMA_InitType. */
  tmpReg0 |= (uint32_t)(DMA_InitType->DMA_ReqSource << DMA_CH0_CFG0_REQ_SEL_Pos);

  /* Store the new value. */
  DMA_CH_CFG0(DMA_Channel) = tmpReg0;

  /* Read the value of the DMA channel0 config1 register. */
  tmpReg1 = DMA_CH_CFG1(DMA_Channel);

  /* Clear DMA DATA_NUM,DATA_SIZE,SRC_INC_MODE,DST_INC_MODE bits. */
  tmpReg1 &= ~(DMA_CH0_CFG1_DATA_NUM_Msk | DMA_CH0_CFG1_DATA_SIZE_Msk \
               | DMA_CH0_CFG1_SRC_INC_MODE_Msk | DMA_CH0_CFG1_DST_INC_MODE_Msk);

  /* Set DMA DATA_NUM[15:0] bits according to DMA_InitType. */
  tmpReg1 |= (uint32_t)(DMA_InitType->DMA_DataNum << DMA_CH0_CFG1_DATA_NUM_Pos);

  /* Set DMA DATA_Size[1:0] bits according to DMA_InitType. */
  tmpReg1 |= (uint32_t)(DMA_InitType->DMA_DataSize << DMA_CH0_CFG1_DATA_SIZE_Pos);

  /* Set DMA SRC_INC_MODE bit according to DMA_InitType. */
  tmpReg1 |= (uint32_t)(DMA_InitType->DMA_SrcIncMode << DMA_CH0_CFG1_SRC_INC_MODE_Pos);

  /* Set DMA DST_INC_MODE bit according to DMA_InitType. */
  tmpReg1 |= (uint32_t)(DMA_InitType->DMA_DstIncMode << DMA_CH0_CFG1_DST_INC_MODE_Pos);

  /* Clear DMA ROUND_NUM[11:0] bits. */
  tmpReg1 &= ~DMA_CH0_CFG1_ROUND_NUM_Msk;

  /* Set DMA ROUND_NUM[11:0] bits according to DMA_InitType. */
  tmpReg1 |= (uint32_t)(DMA_InitType->DMA_RoundNum << DMA_CH0_CFG1_ROUND_NUM_Pos);

  /* Store the new value. */
  DMA_CH_CFG1(DMA_Channel) = tmpReg1;

  /* Sets the DMA source address register value. */
  DMA_CH_SRC_ADDR(DMA_Channel) = DMA_InitType->DMA_SrcAddr;

  /* Sets the DMA destination address register value. */
  DMA_CH_DST_ADDR(DMA_Channel) = DMA_InitType->DMA_DstAddr;
}

/**
 *******************************************************************************
 * @brief   Initialize the DMA_InitType with default parameters.
 * @param   [in]  DMA_InitType: Pointer to a DMA_InitTypeStruct structure
 *                which will be initialized.
 * @retval  None.
 ******************************************************************************/
void DMA_StructInit(DMA_InitTypeStruct * DMA_InitType)
{
  DMA_InitType->DMA_SrcAddr = 0x00000000u;
  DMA_InitType->DMA_DstAddr = 0x00000000u;
  DMA_InitType->DMA_DataNum = 0u;
  DMA_InitType->DMA_RoundNum = 0u;
  DMA_InitType->DMA_DataSize = DMA_DATA_SIZE_8;
  DMA_InitType->DMA_SrcIncMode = DMA_ADDR_MODE_RESET;
  DMA_InitType->DMA_DstIncMode = DMA_ADDR_MODE_RESET;
  DMA_InitType->DMA_ReqSource = DMA_SW_TRIG_REQ;
  DMA_InitType->DMA_CICRState = DISABLE;
  DMA_InitType->DMA_ErrIntState = DISABLE;
  DMA_InitType->DMA_FISIntState = DISABLE;
}

/**
 *******************************************************************************
 * @brief   Enables or disables the specified DMA peripheral.
 * @param   [in]  DMA_Channel: Specifies the DMA channel.
 *                This parameter can be a value of @ref DMA_ChannelEnum.
 *                  @arg DMA_CH0: DMA channel 0.
 *                  @arg DMA_CH1: DMA channel 1.
 *                  @arg DMA_CH2: DMA channel 2.
 *                  @arg DMA_CH3: DMA channel 3.
 *                  @arg DMA_CH4: DMA channel 4.
 * @param   [in]  Newstate: New state of the DMA functions.
 *                This parameter can be: ENABLE or DISABLE.
 * @retval  None.
 ******************************************************************************/
void DMA_Cmd(DMA_ChannelEnum DMA_Channel, FunctionalState Newstate)
{
  if(DISABLE != Newstate)
  {
    /* Enable DMA channel functions. */
    DMA_CH_CFG0(DMA_Channel) |= (uint32_t)DMA_CH0_CFG0_EN_Msk;
  }
  else
  {
    /* Disable DMA channel functions. */
    DMA_CH_CFG0(DMA_Channel) &= ~(uint32_t)DMA_CH0_CFG0_EN_Msk;
  }
}

/**
 *******************************************************************************
 * @brief   Software trigger transmit.
 * @param   [in]  DMA_Channel: Specifies the DMA channel.
 *                This parameter can be a value of @ref DMA_ChannelEnum.
 *                  @arg DMA_CH0: DMA channel 0.
 *                  @arg DMA_CH1: DMA channel 1.
 *                  @arg DMA_CH2: DMA channel 2.
 *                  @arg DMA_CH3: DMA channel 3.
 *                  @arg DMA_CH4: DMA channel 4.
 * @retval  None.
 ******************************************************************************/
void DMA_SoftTriggerTransmit(DMA_ChannelEnum DMA_Channel)
{
  /* Software trigger DMA channel transmit. */
  DMA_CH_CFG0(DMA_Channel) |= (uint32_t)DMA_CH0_CFG0_SW_TRIG_Msk;
}

/**
 *******************************************************************************
 * @brief   Set the amount of data to be transfer by the current DMA channel.
 *          DMA transfers (DataNum+1) data after receiving a DMA request each time.
 * @param   [in]  DMA_Channel: Specifies the DMA channel.
 *                This parameter can be a value of @ref DMA_ChannelEnum.
 *                  @arg DMA_CH0: DMA channel 0.
 *                  @arg DMA_CH1: DMA channel 1.
 *                  @arg DMA_CH2: DMA channel 2.
 *                  @arg DMA_CH3: DMA channel 3.
 *                  @arg DMA_CH4: DMA channel 4.
 * @param   [in]  DataNumber: DMA Channelx transfer data number.
 *                This parameter can be a value of 1-65536.
 * @retval  None.
 ******************************************************************************/
void DMA_SetDataNum(DMA_ChannelEnum DMA_Channel, uint16_t DataNum)
{
  uint32_t tmpReg = 0UL;

  /* Read the value of the DMA channel config1 register. */
  tmpReg = DMA_CH_CFG1(DMA_Channel);

  /* Clear DMA DATA_NUM[15:0] bits. */
  tmpReg &= ~DMA_CH0_CFG1_DATA_NUM_Msk;

  /* Set DMA DATA_NUM[15:0] bits according to DataNumber. */
  tmpReg |= (uint32_t)(DataNum << DMA_CH0_CFG1_DATA_NUM_Pos);

  /* Store the new value. */
  DMA_CH_CFG1(DMA_Channel) = tmpReg;
}

/**
 *******************************************************************************
 * @brief   Set the number of rounds of repeated transmission for the current DMA channel.
 * @param   [in]  DMA_Channel: Specifies the DMA channel.
 *                This parameter can be a value of @ref DMA_ChannelEnum.
 *                  @arg DMA_CH0: DMA channel 0.
 *                  @arg DMA_CH1: DMA channel 1.
 *                  @arg DMA_CH2: DMA channel 2.
 *                  @arg DMA_CH3: DMA channel 3.
 *                  @arg DMA_CH4: DMA channel 4.
 * @retval  uint16_t: The number of remaining data units in the current DMA Channelx transfer.
 ******************************************************************************/
uint16_t DMA_GetDataNum(DMA_ChannelEnum DMA_Channel)
{
  /* Return the number of remaining data units for DMA Channel. */
  return (uint16_t)(DMA_CH_CFG1(DMA_Channel) >> DMA_CH0_CFG1_DATA_NUM_Pos);
}

/**
 *******************************************************************************
 * @brief   Sets the round of data units in the current DMA Channelx transfer.
 *          Transmission (RoundNum+1) round.
 * @param   [in]  DMA_Channel: Specifies the DMA channel.
 *                This parameter can be a value of @ref DMA_ChannelEnum.
 *                  @arg DMA_CH0: DMA channel 0.
 *                  @arg DMA_CH1: DMA channel 1.
 *                  @arg DMA_CH2: DMA channel 2.
 *                  @arg DMA_CH3: DMA channel 3.
 *                  @arg DMA_CH4: DMA channel 4.
 * @param   [in]  DataRound: DMA Channelx transfer data round.
 *                This parameter can be a value of 1-4096.
 * @retval  None.
 ******************************************************************************/
void DMA_SetRoundNum(DMA_ChannelEnum DMA_Channel, uint16_t RoundNum)
{
  uint32_t tmpReg = 0UL;

  /* Read the value of the DMA channel config 1 register. */
  tmpReg = DMA_CH_CFG1(DMA_Channel);

  /* Clear DMA ROUND_NUM[11:0] bits. */
  tmpReg &= ~DMA_CH0_CFG1_ROUND_NUM_Msk;

  /* Set DMA ROUND_NUM[11:0] bits according to DataNumber. */
  tmpReg |= (uint32_t)(RoundNum << DMA_CH0_CFG1_ROUND_NUM_Pos);

  /* Store the new value. */
  DMA_CH_CFG1(DMA_Channel) = tmpReg;
}

/**
 *******************************************************************************
 * @brief   Get the number of rounds to be repeated for the current DMA channel.
 * @param   [in]  DMA_Channel: Specifies the DMA channel.
 *                This parameter can be a value of @ref DMA_ChannelEnum.
 *                  @arg DMA_CH0: DMA channel 0.
 *                  @arg DMA_CH1: DMA channel 1.
 *                  @arg DMA_CH2: DMA channel 2.
 *                  @arg DMA_CH3: DMA channel 3.
 *                  @arg DMA_CH4: DMA channel 4.
 * @retval  uint16_t: The round of data units in the current DMAy Channelx transfer
 ******************************************************************************/
uint16_t DMA_GetRoundNum(DMA_ChannelEnum DMA_Channel)
{
  /* Return the round of data units for DMA Channelx. */
  return (uint16_t)(DMA_CH_CFG1(DMA_Channel) >> DMA_CH0_CFG1_ROUND_NUM_Pos);
}

/**
 *******************************************************************************
 * @brief   Enables or disables the specified DMA interrupts.
 * @param   [in]  DMA_Channel: Specifies the DMA channel.
 *                This parameter can be a value of @ref DMA_ChannelEnum.
 *                  @arg DMA_CH0: DMA channel 0.
 *                  @arg DMA_CH1: DMA channel 1.
 *                  @arg DMA_CH2: DMA channel 2.
 *                  @arg DMA_CH3: DMA channel 3.
 *                  @arg DMA_CH4: DMA channel 4.
 * @param   [in]  DMA_IntType: Specifies the interrupt type.
 *                This parameter can be a value of @ref DMA_IntTypeEnum.
 *                  @arg DMA_INT_TYPE_FINISH: DMA transmit finish interrupt.
 *                  @arg DMA_INT_TYPE_ERROR: DMA transmit error interrupt.
 * @param   [in]  NewState: New state of the DMA interrupts.
 *                This parameter can be: ENABLE or DISABLE.
 * @retval  None.
 ******************************************************************************/
void DMA_IntCmd(DMA_ChannelEnum DMA_Channel, DMA_IntTypeEnum DMA_IntType, FunctionalState NewState)
{
  if(DISABLE != NewState)
  {
    /* Enable DMA channle interrupt for selected. */
    DMA_CH_CFG0(DMA_Channel) |= (uint32_t)DMA_IntType;
  }
  else
  {
    /* Disbale DMA channle interrupt for selected. */
    DMA_CH_CFG0(DMA_Channel) &= ~(uint32_t)DMA_IntType;
  }
}

/**
 *******************************************************************************
 * @brief   Get specifies DMA channel interrupt enable status.
 * @param   [in]  DMA_Channel: Specifies the DMA channel.
 *                This parameter can be a value of @ref DMA_ChannelEnum.
 *                  @arg DMA_CH0: DMA channel 0.
 *                  @arg DMA_CH1: DMA channel 1.
 *                  @arg DMA_CH2: DMA channel 2.
 *                  @arg DMA_CH3: DMA channel 3.
 *                  @arg DMA_CH4: DMA channel 4.
 * @param   [in]  DMA_IntType: Specifies the interrupt type.
 *                This parameter can be a value of @ref DMA_IntTypeEnum.
 *                  @arg DMA_INT_TYPE_FINISH: DMA transmit finish interrupt.
 *                  @arg DMA_INT_TYPE_ERROR: DMA transmit error interrupt.
 * @retval  FunctionalState: The DMA interrupt state(ENABLE or DISABLE).
 ******************************************************************************/
FunctionalState DMA_GetIntCmdStatus(DMA_ChannelEnum DMA_Channel, DMA_IntTypeEnum DMA_IntType)
{
  /* Return the status of the interrupt enable bit. */
  return (FunctionalState)((DMA_CH_CFG0(DMA_Channel) & (DMA_IntType))
                           ? ENABLE : DISABLE);
}

/**
 *******************************************************************************
 * @brief   Get the status of the specified DMA interrupts.
 * @param   [in]  DMA_Channel: Specifies the DMA channel.
 *                This parameter can be a value of @ref DMA_ChannelEnum.
 *                  @arg DMA_CH0: DMA channel 0.
 *                  @arg DMA_CH1: DMA channel 1.
 *                  @arg DMA_CH2: DMA channel 2.
 *                  @arg DMA_CH3: DMA channel 3.
 *                  @arg DMA_CH4: DMA channel 4.
 * @param   [in]  DMA_IntFlag: Specifies the interrupt flag.
 *                This parameter can be a value of @ref DMA_IntFlagEnum.
 *                  @arg DMA_INT_FLAG_FINISH: DMA transmit finish interrupt.
 *                  @arg DMA_INT_FLAG_ERROR: DMA transmit error interrupt.
 * @retval  FlagState: The new state of DMA_IntFlag (SET or RESET).
 ******************************************************************************/
FlagState DMA_GetIntFlagStatus(DMA_ChannelEnum DMA_Channel, DMA_IntFlagEnum DMA_IntFlag)
{
  FlagState state = RESET;

  /* Get the status of the Interrupt */
  if(RESET != (DMA_SR() & ((uint32_t)0x01u << (DMA_IntFlag + DMA_Channel * 4u))))
  {
    state = SET;
  }
  else
  {
    state = RESET;
  }

  /* Return the status of the DMA interrupt flag. */
  return state;
}

/**
 *******************************************************************************
 * @brief   Clear the specified DMA interrupts.
 * @param   [in]  DMA_Channel: Specifies the DMA channel.
 *                This parameter can be a value of @ref DMA_ChannelEnum.
 *                  @arg DMA_CH0: DMA channel 0.
 *                  @arg DMA_CH1: DMA channel 1.
 *                  @arg DMA_CH2: DMA channel 2.
 *                  @arg DMA_CH3: DMA channel 3.
 *                  @arg DMA_CH4: DMA channel 4.
 * @param   [in]  DMA_IntFlag: Specifies the interrupt flag.
 *                This parameter can be a value of @ref DMA_IntFlagEnum.
 *                  @arg DMA_INT_FLAG_FINISH: DMA transmit finish interrupt.
 *                  @arg DMA_INT_FLAG_ERROR: DMA transmit error interrupt.
 * @retval  None.
 ******************************************************************************/
void DMA_ClearIntFlag(DMA_ChannelEnum DMA_Channel, DMA_IntFlagEnum DMA_IntFlag)
{
  /* Clear interrupt flags.*/
  DMA_SR() = ((uint32_t)0x01u << (DMA_IntFlag + DMA_Channel * 4u));
}

/**
 *******************************************************************************
 * @brief   Get DMA transmit status.
 * @param   [in]  DMA_Channel: Specifies the DMA channel.
 *                This parameter can be a value of @ref DMA_ChannelEnum.
 *                  @arg DMA_CH0: DMA channel 0.
 *                  @arg DMA_CH1: DMA channel 1.
 *                  @arg DMA_CH2: DMA channel 2.
 *                  @arg DMA_CH3: DMA channel 3.
 *                  @arg DMA_CH4: DMA channel 4.
 * @retval  DMA_TransmitStatusEnum Transmit status.
 *                This parameter can be a value of @ref DMA_ChannelEnum.
 *                  @arg DMA_TRANSMIT_STATUS_IDLE: DMA transmit idle.
 *                  @arg DMA_TRANSMIT_STATUS_SRCADDR_ERR: DMA transmit source address err.
 *                  @arg DMA_TRANSMIT_STATUS_DSTADDR_ERR: DMA transmit destination address err.
 *                  @arg DMA_TRANSMIT_STATUS_BUSY: DMA transmit busy.
 ******************************************************************************/
DMA_TransmitStatusEnum DMA_GetTransmitStatus(DMA_ChannelEnum DMA_Channel)
{
  /* Return transmit status. */
  return (DMA_TransmitStatusEnum)((DMA_SR() >> (DMA_Channel * 4u + 0x02u)) & 0x03);
}

#endif /* DPM32M08x || DPM32M05x */
