/*
* Copyright (c) 2023-2028, SpaceTouch Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
* hwtimer hal drvier
*
* Change Logs:
* Date          Author      Notes
* 2023-04-18    dongx       the first version
*/

#include "platform.h"
#include "hal_common.h"
#include "hal_cmu.h"
#include "hal_timer.h"

/*the default hwtimer clk source*/
#define HWTIMER_CLK_SRC_DEFAULT             CMU_Clock_OSC_XTAL

/**
 * this function will init the timer clk
 *
 */
void hal_tim_init(void)
{
    int tim_n;
    DEV_CMU->CLKEN0_f.TIMER = 1;
    DEV_RMU->RSTEN0_f.TIMER = 1;

    for (tim_n = 0; tim_n < TIMx_MAX; tim_n++)
    {
        cmu_timer_set_clk(tim_n, HWTIMER_CLK_SRC_DEFAULT);
    }
}

/**
 * this function will disble the timer
 *
 */

void hal_tim_deinit(void)
{
    DEV_CMU->CLKEN0_f.TIMER = 0;
    DEV_RMU->RSTEN0_f.TIMER = 0;
}

/**
 * this function will set the timer mode
 *
 * @param tim_n: timer num
 * @param mode: timer mode,  timer\couter\capture mode
 *
 * @return: none.
 */
void hal_tim_mode_config(tim_base_t tim_n, tim_mode_t mode)
{
    hal_assert(
        (tim_n == TIM0) ||
        (tim_n == TIM1) ||
        (tim_n == TIM2) ||
        (tim_n == TIM3));
    hal_assert(
        (mode == TIM_MODE_TIMER)    ||
        (mode == TIM_MODE_COUNTER)  ||
        (mode == TIM_MODE_CAPTURE));
    DEV_TIMER->TIMER[tim_n].CTL &= ~DEV_TIMER_CTL_MODE_MASK;
    DEV_TIMER->TIMER[tim_n].CTL |= mode;
}

/**
 * this function will set the prediv para
 *
 * @param tim_n: timer num
 * @param div: pre div para, 0 ~ 31
 *
 * @return: none.
 */

void hal_tim_prediv_config(tim_base_t tim_n, tim_clock_prescale_t div)
{
    hal_assert(
        (tim_n == TIM0) ||
        (tim_n == TIM1) ||
        (tim_n == TIM2) ||
        (tim_n == TIM3));
    DEV_TIMER->TIMER[tim_n].CTL &= ~DEV_TIMER_CTL_PREDIVL_MASK;
    DEV_TIMER->TIMER[tim_n].CTL |= div;
}

/**
 * this function will set the gpio source, the gpio source can be used in timer
 * counter and capture mode.
 *
 * @param tim_n: timer num
 * @param gpio: gpio pin,  gpio0~gpio31
 *
 * @return: none.
 */

void hal_tim_gpio_src_config(tim_base_t tim_n, tim_gpio_src_t gpio)
{
    hal_assert(
        (tim_n == TIM0) ||
        (tim_n == TIM1) ||
        (tim_n == TIM2) ||
        (tim_n == TIM3));
    DEV_TIMER->TIMER[tim_n].CTL &= ~DEV_TIMER_CTL_GPIO_SRC_MASK;
    DEV_TIMER->TIMER[tim_n].CTL |= gpio;
}

/**
 * this function will set the gpio debouce
 * counter and capture mode.
 *
 * @param tim_n: timer num
 * @param gpio: gpio pin,  gpio0~gpio31
 *
 * @return: none.
 */
void hal_tim_gpio_debounce_config(tim_base_t tim_n, tim_debounce_t deb_time)
{
    hal_assert(
        (tim_n == TIM0) ||
        (tim_n == TIM1) ||
        (tim_n == TIM2) ||
        (tim_n == TIM3));
    hal_assert(
        (deb_time == TIM_DEBOUNCE_0_CYCLE)   ||
        (deb_time == TIM_DEBOUNCE_24_CYCLE)  ||
        (deb_time == TIM_DEBOUNCE_48_CYCLE)  ||
        (deb_time == TIM_DEBOUNCE_96_CYCLE)  ||
        (deb_time == TIM_DEBOUNCE_192_CYCLE) ||
        (deb_time == TIM_DEBOUNCE_384_CYCLE) ||
        (deb_time == TIM_DEBOUNCE_768_CYCLE) ||
        (deb_time == TIM_DEBOUNCE_1536_CYCLE));
    DEV_TIMER->TIMER[tim_n].CTL &= ~DEV_TIMER_CTL_DEBOUNCE_MASK;
    DEV_TIMER->TIMER[tim_n].CTL |= deb_time;
}
/**
 * this function will set timer enable or disable the reload mode
 * counter and capture mode.
 *
 * @param tim_n: timer num
 * @param reload: enbale or disable the reload mode
 *
 * @return: none.
 */
void hal_tim_reload_config(tim_base_t tim_n, tim_reload_t reload)
{
    hal_assert(
        (tim_n == TIM0) ||
        (tim_n == TIM1) ||
        (tim_n == TIM2) ||
        (tim_n == TIM3));
    hal_assert(
        (reload == TIM_RELOAD_DISABLE)  ||
        (reload == TIM_RELOAD_ENABLE));
    DEV_TIMER->TIMER[tim_n].CTL &= ~DEV_TIMER_CTL_RELOAD_EN_MASK;
    DEV_TIMER->TIMER[tim_n].CTL |= reload;
}

/**
 * this function will set the gpio trigger edge
 *
 * @param tim_n: timer num
 * @param edge: gpio edge trigger mode, including RISE/FAIL/BOTH.
 *
 * @return: none.
 */
void hal_tim_edge_config(tim_base_t tim_n, tim_edge_t edge)
{
    hal_assert(
        (tim_n == TIM0) ||
        (tim_n == TIM1) ||
        (tim_n == TIM2) ||
        (tim_n == TIM3));
    DEV_TIMER->TIMER[tim_n].CTL &= ~(DEV_TIMER_CTL_EDGE_MASK |
                                     DEV_TIMER_CTL_CAP_POL_MASK);
    DEV_TIMER->TIMER[tim_n].CTL |= edge;
}

/**
 * this function will set timer counter increase dirction
 *
 * @param tim_n: timer num
 * @param direction: increase mode/decrease mode
 *
 * @return: none.
 */
void hal_tim_counter_dire_config(tim_base_t tim_n, tim_counter_dire_t direction)
{
    hal_assert(
        (tim_n == TIM0) ||
        (tim_n == TIM1) ||
        (tim_n == TIM2) ||
        (tim_n == TIM3));
    hal_assert(
        (direction == TIM_DIRECTION_INCREASE)   ||
        (direction == TIM_DIRECTION_DECREASE));
    DEV_TIMER->TIMER[tim_n].CTL &= ~DEV_TIMER_CTL_DIRECTION_MASK;
    DEV_TIMER->TIMER[tim_n].CTL |= direction;
}

/**
 * this function will start the timer
 *
 * @param tim_n: timer num
 * @param direction: DISABLE/ENABLE
 *
 * @return: none.
 */
void hal_tim_run_cmd(tim_base_t tim_n, uint32_t en)
{
    hal_assert(
        (tim_n == TIM0) ||
        (tim_n == TIM1) ||
        (tim_n == TIM2) ||
        (tim_n == TIM3));
    hal_assert((en == DISABLE) || (en == ENABLE));
    DEV_TIMER->TIMER[tim_n].CTL_f.RUN = en;
}

/**
 * this function will restart the timer, it will reset the timer
 *
 * @param tim_n: hwtiemr num
 *
 * @return: none.
 */
void hal_tim_restart(tim_base_t tim_n)
{
    hal_assert(
        (tim_n == TIM0) ||
        (tim_n == TIM1) ||
        (tim_n == TIM2) ||
        (tim_n == TIM3));
    DEV_TIMER->TIMER[tim_n].CTL_f.RESTART = SET;
}

/**
 * this function set the reload value of the timer
 *
 * @param tim_n: timer num
 * @param value: reload valure
 *
 * @return: none.
 */
void hal_tim_set_reload_value(tim_base_t tim_n, uint32_t value)
{
    hal_assert(
        (tim_n == TIM0) ||
        (tim_n == TIM1) ||
        (tim_n == TIM2) ||
        (tim_n == TIM3));
    DEV_TIMER->TIMER[tim_n].LEN = value;
}

/**
 * this function get the counter reg of the timer
 *
 * @param tim_n: timer num
 *
 * @return: current counter value.
 */
uint32_t hal_tim_get_counter_value(tim_base_t tim_n)
{
    hal_assert(
        (tim_n == TIM0) ||
        (tim_n == TIM1) ||
        (tim_n == TIM2) ||
        (tim_n == TIM3));
    return DEV_TIMER->TIMER[tim_n].VAL;
}

/**
 * this function get the capture value of the timer
 *
 * @param tim_n: timer num
 *
 * @return: capture val.
 */
uint32_t hal_tim_get_capture_value(tim_base_t tim_n)
{
    hal_assert(
        (tim_n == TIM0) ||
        (tim_n == TIM1) ||
        (tim_n == TIM2) ||
        (tim_n == TIM3));
    return DEV_TIMER->TIMER[tim_n].CAP;
}


/**
 * this function enable the capture dma request
 *
 * @param tim_n: timer num
 *
 * @return: none
 */
void hal_tim_capture_dma_enable(tim_base_t tim_n)
{
    hal_assert(
        (tim_n == TIM0) ||
        (tim_n == TIM1) ||
        (tim_n == TIM2) ||
        (tim_n == TIM3));
    DEV_TIMER->IE |= (TIM_CAPTURE_DMA(tim_n));
}

/**
 * this function disable the capture dma request
 *
 * @param tim_n: timer num
 *
 * @return: none
 */
void hal_tim_capture_dma_disable(tim_base_t tim_n)
{
    hal_assert(
        (tim_n == TIM0) ||
        (tim_n == TIM1) ||
        (tim_n == TIM2) ||
        (tim_n == TIM3));
    DEV_TIMER->IE &= ~(TIM_CAPTURE_DMA(tim_n));
}

/**
 * this function enable the capture interrupt request
 *
 * @param tim_n: timer num
 *
 * @return: none
 */
void hal_tim_capture_interrupt_enable(tim_base_t tim_n)
{
    hal_assert(
        (tim_n == TIM0) ||
        (tim_n == TIM1) ||
        (tim_n == TIM2) ||
        (tim_n == TIM3));
    DEV_TIMER->IE |= (TIM_IT_CAPTURE(tim_n));
}

/**
 * this function disable the capture interrupt request
 *
 * @param tim_n: timer num
 *
 * @return: none
 */
void hal_tim_capture_interrupt_disable(tim_base_t tim_n)
{
    hal_assert(
        (tim_n == TIM0) ||
        (tim_n == TIM1) ||
        (tim_n == TIM2) ||
        (tim_n == TIM3));
    DEV_TIMER->IE &= ~(TIM_IT_CAPTURE(tim_n));
}

/**
 * this function enable the timer/counter interrupt
 *
 * @param tim_n: timer num
 *
 * @return: none
 */
void hal_tim_timer_interrupt_enable(tim_base_t tim_n)
{
    hal_assert(
        (tim_n == TIM0) ||
        (tim_n == TIM1) ||
        (tim_n == TIM2) ||
        (tim_n == TIM3));
    DEV_TIMER->IE |= (TIM_IT_TIMER(tim_n));
}

/**
 * this function disable the timer/counter interrupt
 *
 * @param tim_n: timer num
 *
 * @return: none
 */
void hal_tim_timer_interrupt_disable(tim_base_t tim_n)
{
    hal_assert(
        (tim_n == TIM0) ||
        (tim_n == TIM1) ||
        (tim_n == TIM2) ||
        (tim_n == TIM3));
    DEV_TIMER->IE &= ~(TIM_IT_TIMER(tim_n));
}

/**
 * this function get the timer interrupt pending
 *
 * @param tim_n: timer num
 *
 * @return: timer pending value
 */
uint32_t hal_tim_get_interrupt_pending(void)
{
    return DEV_TIMER->PD;
}

/**
 * this function clear the timer interrupt pending
 *
 * @param tim_n: timer num
 *
 * @return: none
 */
void hal_tim_clear_interrupt_pending(uint32_t tim_it_s)
{
    hal_assert(0 == (tim_it_s & (~(
                                     TIM_IT_TIMER0   |
                                     TIM_IT_TIMER1   |
                                     TIM_IT_TIMER2   |
                                     TIM_IT_TIMER3   |
                                     TIM_IT_CAPTURE0 |
                                     TIM_IT_CAPTURE1 |
                                     TIM_IT_CAPTURE2 |
                                     TIM_IT_CAPTURE3))));
    DEV_TIMER->PD = tim_it_s;
}
