/*!
    \file  gd32f30x_ctc.c
    \brief CTC driver
*/

/*
    Copyright (C) 2017 GigaDevice

    2017-02-10, V1.0.1, firmware for GD32F30x
*/

#include "gd32f30x_ctc.h"

#define CTC_FLAG_MASK            ((uint32_t)0x00000700U)

/*!
    \brief      reset CTC clock trim controller
    \param[in]  none
    \param[out] none
    \retval     none
*/
void ctc_deinit(void)
{
    /* reset CTC */
    rcu_periph_reset_enable(RCU_CTCRST);
    rcu_periph_reset_disable(RCU_CTCRST);
}

/*!
    \brief      configure the IRC48M trim value
    \param[in]  ctc_trim_value: 8-bit IRC48M trim value
    \param[out] none
    \retval     none
*/
void ctc_irc48m_trim_value_config(uint8_t ctc_trim_value)
{
    /* clear TRIMVALUE bits */
    CTC_CTL0 &= (~(uint32_t)CTC_CTL0_TRIMVALUE);
    /* set TRIMVALUE bits */
    CTC_CTL0 |= ((uint32_t)ctc_trim_value << 8);
}

/*!
    \brief      generate software reference source sync pulse
    \param[in]  none
    \param[out] none
    \retval     none
*/
void ctc_software_refsource_pulse_generate(void)
{
    CTC_CTL0 |= (uint32_t)CTC_CTL0_SWREFPUL;
}

/*!
    \brief      configure hardware automatically trim mode
    \param[in]  ctc_hardmode:
      \arg        CTC_HARDWARE_TRIM_MODE_ENABLE: hardware automatically trim mode enable
      \arg        CTC_HARDWARE_TRIM_MODE_DISABLE: hardware automatically trim mode disable
    \param[out] none
    \retval     none
*/
void ctc_hardware_trim_mode_config(uint32_t ctc_hardmode)
{
    CTC_CTL0 &= (uint32_t)(~CTC_CTL0_AUTOTRIM);
    CTC_CTL0 |= (uint32_t)ctc_hardmode;
}

/*!
    \brief      enable CTC trim counter
    \param[in]  none
    \param[out] none
    \retval     none
*/
void ctc_counter_enable(void)
{
    CTC_CTL0 |= (uint32_t)CTC_CTL0_CNTEN;
}

/*!
    \brief      disable CTC trim counter
    \param[in]  none
    \param[out] none
    \retval     none
*/
void ctc_counter_disable(void)
{
    CTC_CTL0 &= (uint32_t)(~CTC_CTL0_CNTEN);
}

/*!
    \brief      configure reference signal source polarity
    \param[in]  ctc_polarity:
      \arg        CTC_REFSOURCE_POLARITY_FALLING: reference signal source polarity is falling edge
      \arg        CTC_REFSOURCE_POLARITY_RISING: reference signal source polarity is rising edge
    \param[out] none
    \retval     none
*/
void ctc_refsource_polarity_config(uint32_t ctc_polarity)
{
    CTC_CTL1 &= (uint32_t)(~CTC_CTL1_REFPOL);
    CTC_CTL1 |= (uint32_t)ctc_polarity;
}

/*!
    \brief      select reference signal source
    \param[in]  ctc_refs:
      \arg        CTC_REFSOURCE_GPIO: GPIO is selected
      \arg        CTC_REFSOURCE_LXTAL: LXTAL is clock selected
      \arg        CTC_REFSOURCE_USBSOF: USBSOF is selected
    \param[out] none
    \retval     none
*/
void ctc_refsource_signal_select(uint32_t ctc_refs)
{
    CTC_CTL1 &= (uint32_t)(~CTC_CTL1_REFSEL);
    CTC_CTL1 |= (uint32_t)ctc_refs;
}

/*!
    \brief      configure reference signal source prescaler
    \param[in]  ctc_prescaler:
      \arg        CTC_REFSOURCE_PSC_OFF: reference signal not divided
      \arg        CTC_REFSOURCE_PSC_DIV2: reference signal divided by 2
      \arg        CTC_REFSOURCE_PSC_DIV4: reference signal divided by 4
      \arg        CTC_REFSOURCE_PSC_DIV8: reference signal divided by 8
      \arg        CTC_REFSOURCE_PSC_DIV16: reference signal divided by 16
      \arg        CTC_REFSOURCE_PSC_DIV32: reference signal divided by 32
      \arg        CTC_REFSOURCE_PSC_DIV64: reference signal divided by 64
      \arg        CTC_REFSOURCE_PSC_DIV128: reference signal divided by 128
    \param[out] none
    \retval     none
*/
void ctc_refsource_prescaler_config(uint32_t ctc_prescaler)
{
    CTC_CTL1 &= (uint32_t)(~CTC_CTL1_REFPSC);
    CTC_CTL1 |= (uint32_t)ctc_prescaler;
}

/*!
    \brief      configure clock trim base limit value
    \param[in]  ctc_limit_value: 8-bit clock trim base limit value
    \param[out] none
    \retval     none
*/
void ctc_clock_limit_value_config(uint8_t ctc_limit_value)
{
    CTC_CTL1 &= (uint32_t)(~CTC_CTL1_CKLIM);
    CTC_CTL1 |= (uint32_t)((uint32_t)ctc_limit_value << 16);
}

/*!
    \brief      configure CTC counter reload value
    \param[in]  ctc_reload_value: 16-bit CTC counter reload value
    \param[out] none
    \retval     none
*/
void ctc_counter_reload_value_config(uint16_t ctc_reload_value)
{
    CTC_CTL1 &= (uint32_t)(~CTC_CTL1_RLVALUE);
    CTC_CTL1 |= (uint32_t)ctc_reload_value;
}

/*!
    \brief      read CTC counter capture value when reference sync pulse occurred
    \param[in]  none
    \param[out] none
    \retval     the 16-bit CTC counter capture value
*/
uint16_t ctc_counter_capture_value_read(void)
{
    uint16_t capture_value = 0U;
    capture_value = (uint16_t)((CTC_STAT & CTC_STAT_REFCAP)>> 16);
    return (capture_value);
}

/*!
    \brief      read CTC trim counter direction when reference sync pulse occurred
    \param[in]  none
    \param[out] none
    \retval     FlagStatus: SET or RESET
      \arg        SET: CTC trim counter direction is down-counting
      \arg        RESET: CTC trim counter direction is up-counting
*/
FlagStatus ctc_counter_direction_read(void)
{
    if(RESET != (CTC_STAT & CTC_STAT_REFDIR)){
        return SET;
    }else{
        return RESET;
    }
}

/*!
    \brief      read CTC counter reload value
    \param[in]  none
    \param[out] none
    \retval     the 16-bit CTC counter reload value
*/
uint16_t ctc_counter_reload_value_read(void)
{
    uint16_t reload_value = 0U;
    reload_value = (uint16_t)(CTC_CTL1 & CTC_CTL1_RLVALUE);
    return (reload_value);
}

/*!
    \brief      read the IRC48M trim value
    \param[in]  none
    \param[out] none
    \retval     the 8-bit IRC48M trim value
*/
uint8_t ctc_irc48m_trim_value_read(void)
{
    uint8_t trim_value = 0U;
    trim_value = (uint8_t)((CTC_CTL0 & CTC_CTL0_TRIMVALUE) >> 8);
    return (trim_value);
}

/*!
    \brief      enable the CTC interrupt
    \param[in]  ctc_interrupt: CTC interrupt enable
      \arg        CTC_INT_CKOK: clock trim OK interrupt enable
      \arg        CTC_INT_CKWARN: clock trim warning interrupt enable
      \arg        CTC_INT_ERR: error interrupt enable
      \arg        CTC_INT_EREF: expect reference interrupt enable
    \param[out] none
    \retval     none
*/
void ctc_interrupt_enable(uint32_t ctc_interrupt)
{
    CTC_CTL0 |= (uint32_t)ctc_interrupt; 
}

/*!
    \brief      disable the CTC interrupt
    \param[in]  ctc_interrupt: CTC interrupt enable source
      \arg        CTC_INT_CKOK: clock trim OK interrupt enable
      \arg        CTC_INT_CKWARN: clock trim warning interrupt enable
      \arg        CTC_INT_ERR: error interrupt enable
      \arg        CTC_INT_EREF: expect reference interrupt enable
    \param[out] none
    \retval     none
*/
void ctc_interrupt_disable(uint32_t ctc_interrupt)
{
    CTC_CTL0 &= (uint32_t)(~ctc_interrupt); 
}

/*!
    \brief      get CTC interrupt flag
    \param[in]  ctc_interrupt: the CTC interrupt flag
      \arg        CTC_INT_FLAG_CKOK: clock trim OK interrupt
      \arg        CTC_INT_FLAG_CKWARN: clock trim warning interrupt 
      \arg        CTC_INT_FLAG_ERR: error interrupt 
      \arg        CTC_INT_FLAG_EREF: expect reference interrupt
      \arg        CTC_INT_FLAG_CKERR: clock trim error bit interrupt
      \arg        CTC_INT_FLAG_REFMISS: reference sync pulse miss interrupt 
      \arg        CTC_INT_FLAG_TRIMERR: trim value error interrupt
    \param[out] none
    \retval     FlagStatus: SET or RESET
*/
FlagStatus ctc_interrupt_flag_get(uint32_t ctc_interrupt)
{
    uint32_t interrupt = 0U, intenable = 0U;

    if(ctc_interrupt & CTC_FLAG_MASK){
        intenable = CTC_CTL0 & CTC_CTL0_ERRIE;
    }else{
        intenable = CTC_CTL0 & ctc_interrupt;
    }
    interrupt = CTC_STAT & ctc_interrupt;

    if(interrupt && intenable){
        return SET;
    }else{
        return RESET;
    }
}

/*!
    \brief      clear CTC interrupt flag
    \param[in]  ctc_interrupt: the CTC interrupt flag
      \arg        CTC_INT_FLAG_CKOK: clock trim OK interrupt
      \arg        CTC_INT_FLAG_CKWARN: clock trim warning interrupt 
      \arg        CTC_INT_FLAG_ERR: error interrupt 
      \arg        CTC_INT_FLAG_EREF: expect reference interrupt 
      \arg        CTC_INT_FLAG_CKERR: clock trim error bit interrupt
      \arg        CTC_INT_FLAG_REFMISS: reference sync pulse miss interrupt 
      \arg        CTC_INT_FLAG_TRIMERR: trim value error interrupt
    \param[out] none
    \retval     none
*/ 
void ctc_interrupt_flag_clear(uint32_t ctc_interrupt)
{
    if(ctc_interrupt & CTC_FLAG_MASK){
        CTC_INTC |= CTC_INTC_ERRIC;
    }else{
        CTC_INTC |= ctc_interrupt;
    }
}

/*!
    \brief      get CTC flag
    \param[in]  ctc_flag: the CTC flag
      \arg        CTC_FLAG_CKOK: clock trim OK flag
      \arg        CTC_FLAG_CKWARN: clock trim warning flag 
      \arg        CTC_FLAG_ERR: error flag 
      \arg        CTC_FLAG_EREF: expect reference flag
      \arg        CTC_FLAG_CKERR: clock trim error bit
      \arg        CTC_FLAG_REFMISS: reference sync pulse miss
      \arg        CTC_FLAG_TRIMERR: trim value error bit
    \param[out] none
    \retval     FlagStatus: SET or RESET
*/
FlagStatus ctc_flag_get(uint32_t ctc_flag)
{
    if(RESET != (CTC_STAT & ctc_flag)){
        return SET;
    }else{
        return RESET;
    }
}

/*!
    \brief      clear CTC flag
    \param[in]  ctc_flag: the CTC flag
      \arg        CTC_FLAG_CKOK: clock trim OK flag
      \arg        CTC_FLAG_CKWARN: clock trim warning flag 
      \arg        CTC_FLAG_ERR: error flag 
      \arg        CTC_FLAG_EREF: expect reference flag
      \arg        CTC_FLAG_CKERR: clock trim error bit
      \arg        CTC_FLAG_REFMISS: reference sync pulse miss
      \arg        CTC_FLAG_TRIMERR: trim value error bit
    \param[out] none
    \retval     none
*/
void ctc_flag_clear(uint32_t ctc_flag)
{
    if(ctc_flag & CTC_FLAG_MASK){
        CTC_INTC |= CTC_INTC_ERRIC;
    }else{
        CTC_INTC |= ctc_flag;
    }
}
