/***************************************************************************//**
  @ingroup ltimer_port_gd32f450
  @file ports/gd32f450/lclock.c
  Файл содержит реализацию порта для конроллера GD32F450 с использованием
  таймера общего назначения TIMER4 или SysTick
  Порт использует определения из штатной библиотеки Gigadevice и CMSIS.
  
  @author Руслан Кяргин <kyargin@lcard.ru>
 ******************************************************************************/
#include "lclock.h"
/* файл должен определять LPC_SYSCLK и включать cmsis.h */
#include "chip.h"
#include "gd32f4xx_misc.h"

#ifndef LCLOCK_IN_CLK_FREQ
    #ifdef CHIP_CLK_SYSTICK_FREQ
        /* используем частоту SysTick, если она определена в chip */
        #define LCLOCK_IN_CLK_FREQ CHIP_CLK_SYSTICK_FREQ
    #elif defined LPC_SYSCLK
        /* для совместимости с версией chip, где определялось только значение LPC_SYSCLK */
        #define LCLOCK_IN_CLK_FREQ LPC_SYSCLK
    #else
        /* если другие варианты не определены, то используем вариант с внешней
         * переменной, который в частности используется в lpcopen */
        extern uint32_t SystemCoreClock;
        #define LCLOCK_IN_CLK_FREQ SystemCoreClock
    #endif
#endif

volatile t_lclock_ticks lclock_systicks;
static char f_initialized = 0;

/*************************************************************************
 * инициализация системного таймера
 * ***********************************************************************/
void lclock_init_val(t_lclock_ticks init_val) {
    uint32_t ahb_frequency = 0;
    /* инициализируем счетчик */
    /* configure systick clock source as HCLK */

    ahb_frequency = rcu_clock_freq_get(CK_AHB);
    
    lclock_systicks = init_val;
    f_initialized = 1;

#ifdef USE_CM4_SYSTICK
    systick_clksource_set(SYSTICK_CLKSOURCE_HCLK);
    
    SysTick_Config(ahb_frequency / LCLOCK_TICKS_PER_SECOND);
#else
    
    rcu_periph_clock_enable(RCU_TIMER4);
    rcu_periph_reset_enable(RCU_TIMER4RST);
    rcu_periph_reset_disable(RCU_TIMER4RST);

    timer_oc_parameter_struct timer_ocintpara;
    timer_parameter_struct timer_initpara;

    timer_deinit(TIMER4);

    /* TIMER1 тактирует TIMER4 с частотой 1МГц, делаем чтобы TIMER4 тикал с частотой LCLOCK_TICKS_PER_SECOND */
    timer_initpara.prescaler         = (1000000 / LCLOCK_TICKS_PER_SECOND) - 1;
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;
    /* TODO: remove magic value */
    timer_initpara.period            = -1;
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_initpara.repetitioncounter = 0;
    timer_init(TIMER4,&timer_initpara);

    timer_auto_reload_shadow_enable(TIMER4);
    timer_slave_mode_select(TIMER4, TIMER_SLAVE_MODE_EXTERNAL0);
    timer_input_trigger_source_select(TIMER4, TIMER_SMCFG_TRGSEL_ITI0);
    timer_master_slave_mode_config(TIMER4,TIMER_MASTER_SLAVE_MODE_ENABLE);

    TIMER_CNT(TIMER4) = init_val;
    
    timer_disable(TIMER1);
    
    rcu_periph_clock_enable(RCU_TIMER1);
    rcu_periph_reset_enable(RCU_TIMER1RST);
    rcu_periph_reset_disable(RCU_TIMER1RST);
    
    rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);

    int period_1MHz = ahb_frequency / 1000000;
    
    timer_initpara.prescaler         = 0;
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;

    timer_initpara.period            = (period_1MHz - 1);
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_initpara.repetitioncounter = 0;
    timer_init(TIMER1,&timer_initpara);

    /* CH1,CH2 and CH3 configuration in PWM mode */
    timer_ocintpara.ocpolarity  = TIMER_OC_POLARITY_HIGH;
    timer_ocintpara.outputstate = TIMER_CCX_ENABLE;
    timer_ocintpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;
    timer_ocintpara.outputnstate = TIMER_CCXN_DISABLE;
    timer_ocintpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;
    timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;

    timer_channel_output_config(TIMER1,TIMER_CH_0,&timer_ocintpara);

    /* CH1 configuration in PWM mode1,duty cycle 25% */
    timer_channel_output_pulse_value_config(TIMER1,TIMER_CH_0, ((period_1MHz / 2) - 1));
    timer_channel_output_mode_config(TIMER1, TIMER_CH_0,TIMER_OC_MODE_PWM0);
    timer_channel_output_shadow_config(TIMER1, TIMER_CH_0, TIMER_OC_SHADOW_DISABLE);

    timer_master_slave_mode_config(TIMER1,TIMER_MASTER_SLAVE_MODE_ENABLE);
    timer_master_output_trigger_source_select(TIMER1, TIMER_TRI_OUT_SRC_UPDATE);
    timer_single_pulse_mode_config(TIMER1, TIMER_SP_MODE_REPETITIVE);
    
    timer_enable(TIMER4);
    timer_enable(TIMER1);
#endif
}

char lclock_is_initialized(void) {
    return f_initialized;
}

// TODO:
void lclock_disable(void) {
}

#include "lcspec_interrupt.h"
void SysTick_Handler( void )
/* обработка прерывания от системного таймера - обновление числа тиков */
{
#ifdef USE_CM4_SYSTICK
    lclock_systicks++;
#else
    /* При выключенном systick сюда не должны попадать */
    TIMER_CNT(TIMER4) = TIMER_CNT(TIMER4) << 2;
#endif
}
