/**
  **************************************************************************
  * @file     at32f403a_407_crm.c
  * @brief    contains all the functions for the crm firmware library
  **************************************************************************
  *                       Copyright notice & Disclaimer
  *
  * The software Board Support Package (BSP) that is made available to
  * download from Artery official website is the copyrighted work of Artery.
  * Artery authorizes customers to use, copy, and distribute the BSP
  * software and its related documentation for the purpose of design and
  * development in conjunction with Artery microcontrollers. Use of the
  * software is governed by this copyright notice and the following disclaimer.
  *
  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
  *
  **************************************************************************
  */

#include "at32f403a_407_conf.h"

/** @addtogroup AT32F403A_407_periph_driver
  * @{
  */

/** @defgroup CRM
  * @brief CRM driver modules
  * @{
  */

#ifdef CRM_MODULE_ENABLED

/** @defgroup CRM_private_functions
  * @{
  */

/**
  * @brief  reset the crm register
  * @param  none
  * @retval none
  */
void crm_reset(void)
{
  /* reset the crm clock configuration to the default reset state(for debug purpose) */
  /* set hicken bit */
  CRM->ctrl_bit.hicken = TRUE;

  /* wait hick stable */
  while(CRM->ctrl_bit.hickstbl != SET);

  /* hick used as system clock */
  CRM->cfg_bit.sclksel = CRM_SCLK_HICK;

  /* wait sclk switch status */
  while(CRM->cfg_bit.sclksts != CRM_SCLK_HICK);

  /* reset hexten, hextbyps, cfden and pllen bits */
  CRM->ctrl &= ~(0x010D0000U);

  /* reset cfg register, include sclk switch, ahbdiv, apb1div, apb2div, adcdiv,
     clkout pllrcs, pllhextdiv, pllmult, usbdiv and pllrange bits */
  CRM->cfg = 0;

  /* reset clkout[3], usbbufs, hickdiv, clkoutdiv */
  CRM->misc1 = 0;

  /* disable all interrupts enable and clear pending bits  */
  CRM->clkint = 0x009F0000;
}

/**
  * @brief  enable or disable crm low speed external crystal bypass
  * @param  new_state (TRUE or FALSE)
  * @retval none
  */
void crm_lext_bypass(confirm_state new_state)
{
  CRM->bpdc_bit.lextbyps = new_state;
}

/**
  * @brief  enable or disable crm high speed external crystal bypass
  * @param  new_state (TRUE or FALSE)
  * @retval none
  */
void crm_hext_bypass(confirm_state new_state)
{
  CRM->ctrl_bit.hextbyps = new_state;
}

/**
  * @brief  get crm flag status
  * @param  flag
  *         this parameter can be one of the following values:
  *         - CRM_HICK_STABLE_FLAG
  *         - CRM_HEXT_STABLE_FLAG
  *         - CRM_PLL_STABLE_FLAG
  *         - CRM_LEXT_STABLE_FLAG
  *         - CRM_LICK_STABLE_FLAG
  *         - CRM_NRST_RESET_FLAG
  *         - CRM_POR_RESET_FLAG
  *         - CRM_SW_RESET_FLAG
  *         - CRM_WDT_RESET_FLAG
  *         - CRM_WWDT_RESET_FLAG
  *         - CRM_LOWPOWER_RESET_FLAG
  *         interrupt flag:
  *         - CRM_LICK_READY_INT_FLAG
  *         - CRM_LEXT_READY_INT_FLAG
  *         - CRM_HICK_READY_INT_FLAG
  *         - CRM_HEXT_READY_INT_FLAG
  *         - CRM_PLL_READY_INT_FLAG
  *         - CRM_CLOCK_FAILURE_INT_FLAG
  * @retval flag_status (SET or RESET)
  */
flag_status crm_flag_get(uint32_t flag)
{
  flag_status status = RESET;
  if((CRM_REG(flag) & CRM_REG_BIT(flag)) != CRM_REG_BIT(flag))
  {
    status = RESET;
  }
  else
  {
    status = SET;
  }
  return status;
}

/**
  * @brief  get crm interrupt flag status
  * @param  flag
  *         this parameter can be one of the following values:
  *         - CRM_LICK_READY_INT_FLAG
  *         - CRM_LEXT_READY_INT_FLAG
  *         - CRM_HICK_READY_INT_FLAG
  *         - CRM_HEXT_READY_INT_FLAG
  *         - CRM_PLL_READY_INT_FLAG
  *         - CRM_CLOCK_FAILURE_INT_FLAG
  * @retval flag_status (SET or RESET)
  */
flag_status crm_interrupt_flag_get(uint32_t flag)
{
  flag_status status = RESET;
  switch(flag)
  {
    case CRM_LICK_READY_INT_FLAG:
      if(CRM->clkint_bit.lickstblf && CRM->clkint_bit.lickstblien)
      {
        status = SET;
      }
      break;
    case CRM_LEXT_READY_INT_FLAG:
      if(CRM->clkint_bit.lextstblf && CRM->clkint_bit.lextstblien)
      {
        status = SET;
      }
      break;
    case CRM_HICK_READY_INT_FLAG:
      if(CRM->clkint_bit.hickstblf && CRM->clkint_bit.hickstblien)
      {
        status = SET;
      }
      break;
    case CRM_HEXT_READY_INT_FLAG:
      if(CRM->clkint_bit.hextstblf && CRM->clkint_bit.hextstblien)
      {
        status = SET;
      }
      break;
    case CRM_PLL_READY_INT_FLAG:
      if(CRM->clkint_bit.pllstblf && CRM->clkint_bit.pllstblien)
      {
        status = SET;
      }
      break;
    case CRM_CLOCK_FAILURE_INT_FLAG:
      if(CRM->clkint_bit.cfdf && CRM->ctrl_bit.cfden)
      {
        status = SET;
      }
      break;
  }

  return status;
}

/**
  * @brief  wait for hext stable
  * @param  none
  * @retval error_status (ERROR or SUCCESS)
  */
error_status crm_hext_stable_wait(void)
{
  uint32_t stable_cnt = 0;
  error_status status = ERROR;

  while((crm_flag_get(CRM_HEXT_STABLE_FLAG) != SET) && (stable_cnt < HEXT_STARTUP_TIMEOUT))
  {
    stable_cnt ++;
  }

  if(crm_flag_get(CRM_HEXT_STABLE_FLAG) != SET)
  {
    status = ERROR;
  }
  else
  {
    status = SUCCESS;
  }

  return status;
}

/**
  * @brief  set the hick trimming value
  * @param  trim_value (0x00~0x3F)
  * @retval none
  */
void crm_hick_clock_trimming_set(uint8_t trim_value)
{
  CRM->ctrl_bit.hicktrim = trim_value;
}

/**
  * @brief  set the crm calibration value
  * @param  cali_value (0x00~0xFF)
  * @retval none
  */
void crm_hick_clock_calibration_set(uint8_t cali_value)
{
  /* enable write hick calibration */
  CRM->misc1_bit.hickcal_key = 0x5A;

  /* write hick calibration value */
  CRM->ctrl_bit.hickcal = cali_value;

  /* disable write hick calibration */
  CRM->misc1_bit.hickcal_key = 0x0;
}

/**
  * @brief  enable or disable the peripheral clock
  * @param  value
  *         this parameter can be one of the following values:
  *         - CRM_DMA1_PERIPH_CLOCK         - CRM_DMA2_PERIPH_CLOCK         - CRM_CRC_PERIPH_CLOCK         - CRM_XMC_PERIPH_CLOCK
  *         - CRM_SDIO1_PERIPH_CLOCK        - CRM_SDIO2_PERIPH_CLOCK        - CRM_EMAC_PERIPH_CLOCK        - CRM_EMACTX_PERIPH_CLOCK
  *         - CRM_EMACRX_PERIPH_CLOCK       - CRM_EMACPTP_PERIPH_CLOCK      - CRM_IOMUX_PERIPH_CLOCK       - CRM_GPIOA_PERIPH_CLOCK
  *         - CRM_GPIOB_PERIPH_CLOCK        - CRM_GPIOC_PERIPH_CLOCK        - CRM_GPIOD_PERIPH_CLOCK       - CRM_GPIOE_PERIPH_CLOCK
  *         - CRM_ADC1_PERIPH_CLOCK         - CRM_ADC2_PERIPH_CLOCK         - CRM_TMR1_PERIPH_CLOCK        - CRM_SPI1_PERIPH_CLOCK
  *         - CRM_TMR8_PERIPH_CLOCK         - CRM_USART1_PERIPH_CLOCK       - CRM_ADC3_PERIPH_CLOCK        - CRM_TMR9_PERIPH_CLOCK
  *         - CRM_TMR10_PERIPH_CLOCK        - CRM_TMR11_PERIPH_CLOCK        - CRM_ACC_PERIPH_CLOCK         - CRM_I2C3_PERIPH_CLOCK
  *         - CRM_USART6_PERIPH_CLOCK       - CRM_UART7_PERIPH_CLOCK        - CRM_UART8_PERIPH_CLOCK       - CRM_TMR2_PERIPH_CLOCK
  *         - CRM_TMR3_PERIPH_CLOCK         - CRM_TMR4_PERIPH_CLOCK         - CRM_TMR5_PERIPH_CLOCK        - CRM_TMR6_PERIPH_CLOCK
  *         - CRM_TMR7_PERIPH_CLOCK         - CRM_TMR12_PERIPH_CLOCK        - CRM_TMR13_PERIPH_CLOCK       - CRM_TMR14_PERIPH_CLOCK
  *         - CRM_WWDT_PERIPH_CLOCK         - CRM_SPI2_PERIPH_CLOCK         - CRM_SPI3_PERIPH_CLOCK        - CRM_SPI4_PERIPH_CLOCK
  *         - CRM_USART2_PERIPH_CLOCK       - CRM_USART3_PERIPH_CLOCK       - CRM_UART4_PERIPH_CLOCK       - CRM_UART5_PERIPH_CLOCK
  *         - CRM_I2C1_PERIPH_CLOCK         - CRM_I2C2_PERIPH_CLOCK         - CRM_USB_PERIPH_CLOCK         - CRM_CAN1_PERIPH_CLOCK
  *         - CRM_CAN2_PERIPH_CLOCK         - CRM_BPR_PERIPH_CLOCK          - CRM_PWC_PERIPH_CLOCK         - CRM_DAC_PERIPH_CLOCK
  * @param  new_state (TRUE or FALSE)
  * @retval none
  */
void crm_periph_clock_enable(crm_periph_clock_type value, confirm_state new_state)
{
  /* enable periph clock */
  if(TRUE == new_state)
  {
    CRM_REG(value) |= CRM_REG_BIT(value);
  }
  /* disable periph clock */
  else
  {
    CRM_REG(value) &= ~(CRM_REG_BIT(value));
  }
}

/**
  * @brief  enable or disable the peripheral reset
  * @param  value
  *         this parameter can be one of the following values:
  *         - CRM_EMAC_PERIPH_RESET         - CRM_IOMUX_PERIPH_RESET        - CRM_EXINT_PERIPH_RESET       - CRM_GPIOA_PERIPH_RESET
  *         - CRM_GPIOB_PERIPH_RESET        - CRM_GPIOC_PERIPH_RESET        - CRM_GPIOD_PERIPH_RESET       - CRM_GPIOE_PERIPH_RESET
  *         - CRM_ADC1_PERIPH_RESET         - CRM_ADC2_PERIPH_RESET         - CRM_TMR1_PERIPH_RESET        - CRM_SPI1_PERIPH_RESET
  *         - CRM_TMR8_PERIPH_RESET         - CRM_USART1_PERIPH_RESET       - CRM_ADC3_PERIPH_RESET        - CRM_TMR9_PERIPH_RESET
  *         - CRM_TMR10_PERIPH_RESET        - CRM_TMR11_PERIPH_RESET        - CRM_ACC_PERIPH_RESET         - CRM_I2C3_PERIPH_RESET
  *         - CRM_USART6_PERIPH_RESET       - CRM_UART7_PERIPH_RESET        - CRM_UART8_PERIPH_RESET       - CRM_TMR2_PERIPH_RESET
  *         - CRM_TMR3_PERIPH_RESET         - CRM_TMR4_PERIPH_RESET         - CRM_TMR5_PERIPH_RESET        - CRM_TMR6_PERIPH_RESET
  *         - CRM_TMR7_PERIPH_RESET         - CRM_TMR12_PERIPH_RESET        - CRM_TMR13_PERIPH_RESET       - CRM_TMR14_PERIPH_RESET
  *         - CRM_WWDT_PERIPH_RESET         - CRM_SPI2_PERIPH_RESET         - CRM_SPI3_PERIPH_RESET        - CRM_SPI4_PERIPH_RESET
  *         - CRM_USART2_PERIPH_RESET       - CRM_USART3_PERIPH_RESET       - CRM_UART4_PERIPH_RESET       - CRM_UART5_PERIPH_RESET
  *         - CRM_I2C1_PERIPH_RESET         - CRM_I2C2_PERIPH_RESET         - CRM_USB_PERIPH_RESET         - CRM_CAN1_PERIPH_RESET
  *         - CRM_CAN2_PERIPH_RESET         - CRM_BPR_PERIPH_RESET          - CRM_PWC_PERIPH_RESET         - CRM_DAC_PERIPH_RESET
  * @param  new_state (TRUE or FALSE)
  * @retval none
  */
void crm_periph_reset(crm_periph_reset_type value, confirm_state new_state)
{
  /* enable periph reset */
  if(new_state == TRUE)
  {
    CRM_REG(value) |= (CRM_REG_BIT(value));
  }
  /* disable periph reset */
  else
  {
    CRM_REG(value) &= ~(CRM_REG_BIT(value));
  }
}

/**
  * @brief  enable or disable the peripheral clock in sleep mode
  * @param  value
  *         this parameter can be one of the following values:
  *         - CRM_SRAM_PERIPH_CLOCK_SLEEP_MODE
  *         - CRM_FLASH_PERIPH_CLOCK_SLEEP_MODE
  * @param  new_state (TRUE or FALSE)
  * @retval none
  */
void crm_periph_sleep_mode_clock_enable(crm_periph_clock_sleepmd_type value, confirm_state new_state)
{
  /* enable periph clock in sleep mode */
  if(new_state == TRUE)
  {
    CRM_REG(value) |= (CRM_REG_BIT(value));
  }
  /* disable perph clock in sleep mode */
  else
  {
    CRM_REG(value) &= ~(CRM_REG_BIT(value));
  }

}

/**
  * @brief  enable or disable the crm clock source
  * @param  source
  *         this parameter can be one of the following values:
  *         - CRM_CLOCK_SOURCE_HICK
  *         - CRM_CLOCK_SOURCE_HEXT
  *         - CRM_CLOCK_SOURCE_PLL
  *         - CRM_CLOCK_SOURCE_LEXT
  *         - CRM_CLOCK_SOURCE_LICK
  * @param  new_state (TRUE or FALSE)
  * @retval none
  */
void crm_clock_source_enable(crm_clock_source_type source, confirm_state new_state)
{
  switch(source)
  {
    case CRM_CLOCK_SOURCE_HICK:
      CRM->ctrl_bit.hicken = new_state;
      break;
    case CRM_CLOCK_SOURCE_HEXT:
      CRM->ctrl_bit.hexten = new_state;
      break;
    case CRM_CLOCK_SOURCE_PLL:
      CRM->ctrl_bit.pllen = new_state;
      break;
    case CRM_CLOCK_SOURCE_LEXT:
      CRM->bpdc_bit.lexten = new_state;
      break;
    case CRM_CLOCK_SOURCE_LICK:
      CRM->ctrlsts_bit.licken = new_state;
      break;
    default:
      break;
  }
}

/**
  * @brief  clear the crm reset flags
  * @param  flag
  *         this parameter can be one of the following values:
  *         reset flag:
  *         - CRM_NRST_RESET_FLAG
  *         - CRM_POR_RESET_FLAG
  *         - CRM_SW_RESET_FLAG
  *         - CRM_WDT_RESET_FLAG
  *         - CRM_WWDT_RESET_FLAG
  *         - CRM_LOWPOWER_RESET_FLAG
  *         - CRM_ALL_RESET_FLAG
  *         interrupt flag:
  *         - CRM_LICK_READY_INT_FLAG
  *         - CRM_LEXT_READY_INT_FLAG
  *         - CRM_HICK_READY_INT_FLAG
  *         - CRM_HEXT_READY_INT_FLAG
  *         - CRM_PLL_READY_INT_FLAG
  *         - CRM_CLOCK_FAILURE_INT_FLAG
  * @retval none
  */
void crm_flag_clear(uint32_t flag)
{
  switch(flag)
  {
    case CRM_NRST_RESET_FLAG:
    case CRM_POR_RESET_FLAG:
    case CRM_SW_RESET_FLAG:
    case CRM_WDT_RESET_FLAG:
    case CRM_WWDT_RESET_FLAG:
    case CRM_LOWPOWER_RESET_FLAG:
    case CRM_ALL_RESET_FLAG:
      CRM->ctrlsts_bit.rstfc = TRUE;
      while(CRM->ctrlsts_bit.rstfc == TRUE);
      break;
    case CRM_LICK_READY_INT_FLAG:
      CRM->clkint_bit.lickstblfc = TRUE;
      break;
    case CRM_LEXT_READY_INT_FLAG:
      CRM->clkint_bit.lextstblfc = TRUE;
      break;
    case CRM_HICK_READY_INT_FLAG:
      CRM->clkint_bit.hickstblfc = TRUE;
      break;
    case CRM_HEXT_READY_INT_FLAG:
      CRM->clkint_bit.hextstblfc = TRUE;
      break;
    case CRM_PLL_READY_INT_FLAG:
      CRM->clkint_bit.pllstblfc = TRUE;
      break;
    case CRM_CLOCK_FAILURE_INT_FLAG:
      CRM->clkint_bit.cfdfc = TRUE;
      break;
    default:
      break;
  }
}

/**
  * @brief  select rtc clock
  * @param  value
  *         this parameter can be one of the following values:
  *         - CRM_RTC_CLOCK_LEXT
  *         - CRM_RTC_CLOCK_LICK
  *         - CRM_RTC_CLOCK_HEXT_DIV
  * @retval none
  */
void crm_rtc_clock_select(crm_rtc_clock_type value)
{
  CRM->bpdc_bit.rtcsel = value;
}

/**
  * @brief  enable or disable rtc
  * @param  new_state (TRUE or FALSE)
  * @retval none
  */
void crm_rtc_clock_enable(confirm_state new_state)
{
  CRM->bpdc_bit.rtcen = new_state;
}

/**
  * @brief  set crm ahb division
  * @param  value
  *         this parameter can be one of the following values:
  *         - CRM_AHB_DIV_1
  *         - CRM_AHB_DIV_2
  *         - CRM_AHB_DIV_4
  *         - CRM_AHB_DIV_8
  *         - CRM_AHB_DIV_16
  *         - CRM_AHB_DIV_64
  *         - CRM_AHB_DIV_128
  *         - CRM_AHB_DIV_256
  *         - CRM_AHB_DIV_512
  * @retval none
  */
void crm_ahb_div_set(crm_ahb_div_type value)
{
  CRM->cfg_bit.ahbdiv = value;
}

/**
  * @brief  set crm apb1 division
  * @note   the maximum frequency of APB1/APB2 clock is 120 MHz
  * @param  value
  *         this parameter can be one of the following values:
  *         - CRM_APB1_DIV_1
  *         - CRM_APB1_DIV_2
  *         - CRM_APB1_DIV_4
  *         - CRM_APB1_DIV_8
  *         - CRM_APB1_DIV_16
  * @retval none
  */
void crm_apb1_div_set(crm_apb1_div_type value)
{
  CRM->cfg_bit.apb1div = value;
}

/**
  * @brief  set crm apb2 division
  * @note   the maximum frequency of APB1/APB2 clock is 120 MHz
  * @param  value
  *         this parameter can be one of the following values:
  *         - CRM_APB2_DIV_1
  *         - CRM_APB2_DIV_2
  *         - CRM_APB2_DIV_4
  *         - CRM_APB2_DIV_8
  *         - CRM_APB2_DIV_16
  * @retval none
  */
void crm_apb2_div_set(crm_apb2_div_type value)
{
  CRM->cfg_bit.apb2div = value;
}

/**
  * @brief  set crm adc division
  * @param  value
  *         this parameter can be one of the following values:
  *         - CRM_ADC_DIV_2
  *         - CRM_ADC_DIV_4
  *         - CRM_ADC_DIV_6
  *         - CRM_ADC_DIV_8
  *         - CRM_ADC_DIV_12
  *         - CRM_ADC_DIV_16
  * @retval none
  */
void crm_adc_clock_div_set(crm_adc_div_type div_value)
{
  CRM->cfg_bit.adcdiv_l = div_value & 0x03;
  CRM->cfg_bit.adcdiv_h = (div_value >> 2) & 0x01;
}

/**
  * @brief  set crm usb division
  * @param  value
  *         this parameter can be one of the following values:
  *         - CRM_USB_DIV_1_5
  *         - CRM_USB_DIV_1
  *         - CRM_USB_DIV_2_5
  *         - CRM_USB_DIV_2
  *         - CRM_USB_DIV_3_5
  *         - CRM_USB_DIV_3
  *         - CRM_USB_DIV_4
  * @retval none
  */
void crm_usb_clock_div_set(crm_usb_div_type div_value)
{
  CRM->cfg_bit.usbdiv_l = div_value & 0x03;
  CRM->cfg_bit.usbdiv_h = (div_value >> 2) & 0x01;
}

/**
  * @brief  enable or disable clock failure detection
  * @param  new_state (TRUE or FALSE)
  * @retval none
  */
void crm_clock_failure_detection_enable(confirm_state new_state)
{
  CRM->ctrl_bit.cfden = new_state;
}

/**
  * @brief  battery powered domain software reset
  * @param  new_state (TRUE or FALSE)
  * @retval none
  */
void crm_battery_powered_domain_reset(confirm_state new_state)
{
  CRM->bpdc_bit.bpdrst = new_state;
}

/**
  * @brief  config crm pll
  * @param  clock_source
  *         this parameter can be one of the following values:
  *         - CRM_PLL_SOURCE_HICK
  *         - CRM_PLL_SOURCE_HEXT
  *         - CRM_PLL_SOURCE_HEXT_DIV
  * @param  mult_value (CRM_PLL_MULT_2~64)
  * @param  pll_range
  *         this parameter can be one of the following values:
  *         - CRM_PLL_OUTPUT_RANGE_LE72MHZ
  *         - CRM_PLL_OUTPUT_RANGE_GT72MHZ
  * @retval none
  */
void crm_pll_config(crm_pll_clock_source_type clock_source, crm_pll_mult_type mult_value, crm_pll_output_range_type pll_range)
{
  /* config pll clock source */
  if(clock_source == CRM_PLL_SOURCE_HICK)
  {
    CRM->cfg_bit.pllrcs = FALSE;
    CRM->misc1_bit.hickdiv = CRM_HICK48_NODIV;
  }
  else
  {
    CRM->cfg_bit.pllrcs = TRUE;
    if(CRM_PLL_SOURCE_HEXT == clock_source)
    {
      CRM->cfg_bit.pllhextdiv = FALSE;
    }
    else
    {
      CRM->cfg_bit.pllhextdiv = TRUE;
    }
  }

  /* config pll multiplication factor */
  CRM->cfg_bit.pllmult_l = (mult_value & 0x0F);
  CRM->cfg_bit.pllmult_h = ((mult_value & 0x30) >> 4);

  /* config pll output range */
  CRM->cfg_bit.pllrange = pll_range;
}

/**
  * @brief  select system clock source
  * @param  value
  *         this parameter can be one of the following values:
  *         - CRM_SCLK_HICK
  *         - CRM_SCLK_HEXT
  *         - CRM_SCLK_PLL
  * @retval none
  */
void crm_sysclk_switch(crm_sclk_type value)
{
  CRM->cfg_bit.sclksel = value;
  DUMMY_NOP();
}

/**
  * @brief  indicate which clock source is used as system clock
  * @param  none
  * @retval crm_sclk
  *         this return can be one of the following values:
  *         - CRM_SCLK_HICK
  *         - CRM_SCLK_HEXT
  *         - CRM_SCLK_PLL
  */
crm_sclk_type crm_sysclk_switch_status_get(void)
{
  return (crm_sclk_type)CRM->cfg_bit.sclksts;
}

/**
  * @brief  get crm clocks freqency
  * @param  clocks
  *         - pointer to the crm_clocks_freq structure
  * @retval none
  */
void crm_clocks_freq_get(crm_clocks_freq_type *clocks_struct)
{
  uint32_t hext_prediv = 0, pll_mult = 0, pll_mult_h = 0, pll_clock_source = 0, temp = 0, div_value = 0;
  crm_sclk_type sclk_source;

  static const uint8_t sclk_ahb_div_table[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
  static const uint8_t ahb_apb1_div_table[8] = {0, 0, 0, 0, 1, 2, 3, 4};
  static const uint8_t ahb_apb2_div_table[8] = {0, 0, 0, 0, 1, 2, 3, 4};
  static const uint8_t adc_div_table[8] = {2, 4, 6, 8, 2, 12, 8, 16};

  /* get sclk source */
  sclk_source = crm_sysclk_switch_status_get();

  switch(sclk_source)
  {
    case CRM_SCLK_HICK:
      if(((CRM->misc3_bit.hick_to_sclk) != RESET) && ((CRM->misc1_bit.hickdiv) != RESET))
        clocks_struct->sclk_freq = HICK_VALUE * 6;
      else
        clocks_struct->sclk_freq = HICK_VALUE;
      break;
    case CRM_SCLK_HEXT:
      clocks_struct->sclk_freq = HEXT_VALUE;
      break;
    case CRM_SCLK_PLL:
      pll_clock_source = CRM->cfg_bit.pllrcs;
      /* get multiplication factor */
      pll_mult = CRM->cfg_bit.pllmult_l;
      pll_mult_h = CRM->cfg_bit.pllmult_h;

      /* process high bits */
      if((pll_mult_h != 0U) || (pll_mult == 15U))
      {
          pll_mult += ((16U * pll_mult_h) + 1U);
      }
      else
      {
          pll_mult += 2U;
      }

      if (pll_clock_source == 0x00)
      {
        /* hick divided by 2 selected as pll clock entry */
        clocks_struct->sclk_freq = (HICK_VALUE >> 1) * pll_mult;
      }
      else
      {
        /* hext selected as pll clock entry */
        if (CRM->cfg_bit.pllhextdiv != RESET)
        {
          hext_prediv = CRM->misc3_bit.hextdiv;
          /* hext clock divided by 2 */
          clocks_struct->sclk_freq = (HEXT_VALUE / (hext_prediv + 2)) * pll_mult;
        }
        else
        {
          clocks_struct->sclk_freq = HEXT_VALUE * pll_mult;
        }
      }
      break;
    default:
      clocks_struct->sclk_freq = HICK_VALUE;
      break;
  }

  /* compute sclk, ahbclk, abp1clk apb2clk and adcclk frequencies */
  /* get ahb division */
  temp = CRM->cfg_bit.ahbdiv;
  div_value = sclk_ahb_div_table[temp];
  /* ahbclk frequency */
  clocks_struct->ahb_freq = clocks_struct->sclk_freq >> div_value;

  /* get apb1 division */
  temp = CRM->cfg_bit.apb1div;
  div_value = ahb_apb1_div_table[temp];
  /* apb1clk frequency */
  clocks_struct->apb1_freq = clocks_struct->ahb_freq >> div_value;

  /* get apb2 division */
  temp = CRM->cfg_bit.apb2div;
  div_value = ahb_apb2_div_table[temp];
  /* apb2clk frequency */
  clocks_struct->apb2_freq = clocks_struct->ahb_freq >> div_value;

  /* get adc division */
  temp = CRM->cfg_bit.adcdiv_h;
  temp = ((temp << 2) | (CRM->cfg_bit.adcdiv_l));
  div_value = adc_div_table[temp];
  /* adcclk clock frequency */
  clocks_struct->adc_freq = clocks_struct->apb2_freq / div_value;
}

/**
  * @brief  set crm clkout
  * @param  clkout
  *         this parameter can be one of the following values:
  *         - CRM_CLKOUT_NOCLK
  *         - CRM_CLKOUT_LICK
  *         - CRM_CLKOUT_LEXT
  *         - CRM_CLKOUT_SCLK
  *         - CRM_CLKOUT_HICK
  *         - CRM_CLKOUT_HEXT
  *         - CRM_CLKOUT_PLL_DIV_2
  *         - CRM_CLKOUT_PLL_DIV_4
  *         - CRM_CLKOUT_USB
  *         - CRM_CLKOUT_ADC
  * @retval none
  */
void crm_clock_out_set(crm_clkout_select_type clkout)
{
  CRM->cfg_bit.clkout_sel = clkout & 0x7;
  CRM->misc1_bit.clkout_sel = (clkout >> 3) & 0x1;
}

/**
  * @brief  config crm interrupt
  * @param  int
  *         this parameter can be any combination of the following values:
  *         - CRM_LICK_STABLE_INT
  *         - CRM_LEXT_STABLE_INT
  *         - CRM_HICK_STABLE_INT
  *         - CRM_HEXT_STABLE_INT
  *         - CRM_PLL_STABLE_INT
  * @param  new_state (TRUE or FALSE)
  * @retval none
  */
void crm_interrupt_enable(uint32_t crm_int, confirm_state new_state)
{
  if(new_state == TRUE)
    CRM->clkint |= crm_int;
  else
    CRM->clkint &= ~crm_int;
}

/**
  * @brief  auto step clock switch enable
  * @param  new_state (TRUE or FALSE)
  * @retval none
  */
void crm_auto_step_mode_enable(confirm_state new_state)
{
  if(new_state == TRUE)
    CRM->misc3_bit.auto_step_en = CRM_AUTO_STEP_MODE_ENABLE;
  else
    CRM->misc3_bit.auto_step_en = CRM_AUTO_STEP_MODE_DISABLE;
}

/**
  * @brief  usbdev interrupt remapping control
  * @param  int_remap
  *         this parameter can be one of the following values:
  *         - CRM_USB_INT19_INT20
  *         - CRM_USB_INT73_INT74
  * @retval none
  */
void crm_usb_interrupt_remapping_set(crm_usb_int_map_type int_remap)
{
  CRM->intmap_bit.usbintmap = int_remap;
}

/**
  * @brief  config hick divider select
  * @param  value
  *         this parameter can be one of the following values:
  *         - CRM_HICK48_DIV6
  *         - CRM_HICK48_NODIV
  * @retval none
  */
void crm_hick_divider_select(crm_hick_div_6_type value)
{
  CRM->misc1_bit.hickdiv = value;
}

/**
  * @brief  hick as system clock frequency select
  * @param  value
  *         this parameter can be one of the following values:
  *         - CRM_HICK_SCLK_8MHZ
  *         - CRM_HICK_SCLK_48MHZ
  * @retval none
  */
void crm_hick_sclk_frequency_select(crm_hick_sclk_frequency_type value)
{
  crm_hick_divider_select(CRM_HICK48_NODIV);
  CRM->misc3_bit.hick_to_sclk = value;
}

/**
  * @brief  usb 48 mhz clock source select
  * @param  value
  *         this parameter can be one of the following values:
  *         - CRM_USB_CLOCK_SOURCE_PLL
  *         - CRM_USB_CLOCK_SOURCE_HICK
  * @retval none
  */
void crm_usb_clock_source_select(crm_usb_clock_source_type value)
{
  if(value == CRM_USB_CLOCK_SOURCE_HICK)
  {
    crm_hick_sclk_frequency_select(CRM_HICK_SCLK_48MHZ);
  }
  CRM->misc3_bit.hick_to_usb = value;
}

/**
  * @brief  enable or disable clkout direct to tmr10 channel 1
  * @param  new_state (TRUE or FALSE)
  * @retval none
  */
void crm_clkout_to_tmr10_enable(confirm_state new_state)
{
  CRM->misc2_bit.clk_to_tmr = new_state;
}

/**
  * @brief  set hext clock division
  * @param  value
  *         this parameter can be one of the following values:
  *         - CRM_HEXT_DIV_2
  *         - CRM_HEXT_DIV_3
  *         - CRM_HEXT_DIV_4
  *         - CRM_HEXT_DIV_5
  * @retval none
  */
void crm_hext_clock_div_set(crm_hext_div_type value)
{
  CRM->misc3_bit.hextdiv = value;
}

/**
  * @brief  set crm clkout division
  * @param  clkout_div
  *         this parameter can be one of the following values:
  *         - CRM_CLKOUT_DIV_1
  *         - CRM_CLKOUT_DIV_2
  *         - CRM_CLKOUT_DIV_4
  *         - CRM_CLKOUT_DIV_8
  *         - CRM_CLKOUT_DIV_16
  *         - CRM_CLKOUT_DIV_64
  *         - CRM_CLKOUT_DIV_128
  *         - CRM_CLKOUT_DIV_256
  *         - CRM_CLKOUT_DIV_512
  * @retval none
  */
void crm_clkout_div_set(crm_clkout_div_type clkout_div)
{
  CRM->misc1_bit.clkoutdiv = clkout_div;
}

#if defined (AT32F407xx)
/**
  * @brief  set emac output pulse width
  * @param  width
  *         this parameter can be one of the following values:
  *         - CRM_EMAC_PULSE_125MS
  *         - CRM_EMAC_PULSE_1SCLK
  * @retval none
  */
void crm_emac_output_pulse_set(crm_emac_output_pulse_type width)
{
  CRM->misc3_bit.emac_pps_sel = width;
}
#endif

/**
  * @}
  */

#endif

/**
  * @}
  */

/**
  * @}
  */
