//******************************************** Включаемые файлы *********************************************

#include "lmcal_cfg.h"
#include "lmcal_cpu_port.h"
#include "lmcal_cpu_cfg.h"

#include "at32f403a_407_conf.h"


//******************************************* Локальные константы *******************************************

// Коэффициенты умножителя и делителя частоты для PLL
#if (8 == LMCAL_CPU_HEXT_CLK)
    #define LMCAL_RCC_PLL_MULT    (CRM_PLL_MULT_60)
    #define LMCAL_RCC_HEXT_DIV    (CRM_HEXT_DIV_2)

#elif (16 == LMCAL_CPU_HEXT_CLK)
    #define LMCAL_RCC_PLL_MULT    (CRM_PLL_MULT_30)
    #define LMCAL_RCC_HEXT_DIV    (CRM_HEXT_DIV_2)

#else
    #error "LMCAL_CPU_HEXT_CLK is not valid!"    
#endif

// Максимальный адрес Flash
#define CPU_PORT_MAX_ADDR       (0x08040000)

//******************************************* Проверка параметров *******************************************


//******************************************** Определения типов ********************************************


//************************************ Глобальные (публичные) переменные ************************************


//************************************* Локальные (приватные) переменные ************************************


//***********************************************************************************************************
//******************************** Определения локальных (приватных) функций ********************************
//***********************************************************************************************************

// Настройка МК на максимальную частоту.
static void f_init_clk_bus(void)
{
    // Частоты в результате настройки:    
    // sclk                = 240000000
    // ahbdiv              = 1
    // ahbclk              = 240000000
    // apb2div             = 2
    // apb2clk             = 120000000
    // apb1div             = 2
    // apb1clk             = 120000000
  
    /* reset crm */
    crm_reset();

    crm_clock_source_enable(CRM_CLOCK_SOURCE_HEXT, TRUE);

    /* wait till hext is ready */
    while(crm_hext_stable_wait() == ERROR)
    {
    }

    /* config pll clock resource */
    crm_pll_config(CRM_PLL_SOURCE_HEXT_DIV, LMCAL_RCC_PLL_MULT, CRM_PLL_OUTPUT_RANGE_GT72MHZ);
    
    /* config hext division */
    crm_hext_clock_div_set(LMCAL_RCC_HEXT_DIV);

    /* enable pll */
    crm_clock_source_enable(CRM_CLOCK_SOURCE_PLL, TRUE);

    /* wait till pll is ready */
    while(crm_flag_get(CRM_PLL_STABLE_FLAG) != SET)
    {
    }

    /* config ahbclk */
    crm_ahb_div_set(CRM_AHB_DIV_1);

    /* config apb2clk, the maximum frequency of APB1/APB2 clock is 120 MHz */
    crm_apb2_div_set(CRM_APB2_DIV_2);

    /* config apb1clk, the maximum frequency of APB1/APB2 clock is 120 MHz  */
    crm_apb1_div_set(CRM_APB1_DIV_2);

    /* enable auto step mode */
    crm_auto_step_mode_enable(TRUE);

    /* select pll as system clock source */
    crm_sysclk_switch(CRM_SCLK_PLL);

    /* wait till pll is used as system clock source */
    while(crm_sysclk_switch_status_get() != CRM_SCLK_PLL)
    {
    }

    /* disable auto step mode */
    crm_auto_step_mode_enable(FALSE);

    /* update system_core_clock global variable */
    system_core_clock_update();
}


static void f_init_adc_clk(void)
{
    crm_adc_clock_div_set(LMCAL_CPU_CLK_BUS3);
}



//***********************************************************************************************************
//******************************** Определения процедур обработки прерываний ********************************
//***********************************************************************************************************


//***********************************************************************************************************
//******************************* Определения глобальных (публичных) функций ********************************
//***********************************************************************************************************

fun_res_t lmcal_cpu_port_init_bus(lmcal_cpu_bus_t num)
{
    fun_res_t err = ERR_OK;

    switch(num)
    {
        case(LMCAL_CPU_BUS0):
            // ahb, abp1, apb2
            f_init_clk_bus();
        case(LMCAL_CPU_BUS1):
        case(LMCAL_CPU_BUS2):
        break;

        case(LMCAL_CPU_BUS3):
            // ADC
            f_init_adc_clk();
        break;

        case(LMCAL_CPU_BUS4):
        case(LMCAL_CPU_BUS5):
        case(LMCAL_CPU_BUS6):
        case(LMCAL_CPU_BUS7):
        case(LMCAL_CPU_BUS8):
        case(LMCAL_CPU_BUS9):
        case(LMCAL_CPU_BUS10):
        case(LMCAL_CPU_BUS11):
        case(LMCAL_CPU_BUS12):
        case(LMCAL_CPU_BUS13):
        case(LMCAL_CPU_BUS14):
        case(LMCAL_CPU_BUS15):
        break;

        default:
            err = ERR_INVALID_ARGUMENT;
        break;
    }

    return err;
}


fun_res_t lmcal_cpu_port_get_bus_frequency(lmcal_cpu_bus_t num, uint32_t* value)
{
    fun_res_t err = ERR_OK;
    crm_clocks_freq_type clocks_freq;

    // Получить частоту шин
    crm_clocks_freq_get(&clocks_freq);

    switch(num)
    {
        case(LMCAL_CPU_BUS0):
            *value = clocks_freq.ahb_freq;
        break;
        case(LMCAL_CPU_BUS1):
            *value = clocks_freq.apb1_freq;
        break;
        case(LMCAL_CPU_BUS2):
            *value = clocks_freq.apb2_freq;
        break;
        case(LMCAL_CPU_BUS3):
            *value = clocks_freq.adc_freq;
        break;

        case(LMCAL_CPU_BUS4):
        case(LMCAL_CPU_BUS5):
        case(LMCAL_CPU_BUS6):
        case(LMCAL_CPU_BUS7):
        case(LMCAL_CPU_BUS8):
        case(LMCAL_CPU_BUS9):
        case(LMCAL_CPU_BUS10):
        case(LMCAL_CPU_BUS11):
        case(LMCAL_CPU_BUS12):
        case(LMCAL_CPU_BUS13):
        case(LMCAL_CPU_BUS14):
        case(LMCAL_CPU_BUS15):
            err = ERR_INVALID_ARGUMENT;
        break;

        default:
            err = ERR_INVALID_ARGUMENT;
        break;
    }

    return err;
}


void lmcal_cpu_port_reset(void)
{
    NVIC_SystemReset();
}


void lmcal_cpu_port_sleep(void)
{
    #warning "TODO lmcal_cpu_port_sleep()"
}


fun_res_t lmcal_cpu_port_interrupt_enable(bool new_state)
{
    fun_res_t err = ERR_OK;

    if(new_state == true)
    {
        __enable_irq();
    }
    else if (new_state == false)
    {
        __disable_irq();
    }
    else
    {
        err = ERR_INVALID_ARGUMENT;
    }

    return err;
}


fun_res_t lmcal_cpu_port_get_uniq_num(u32* uniq_num, u8* size)
{
    fun_res_t err = ERR_NOT_IMPLEMENTED;

    #warning "TODO lmcal_cpu_port_get_uniq_num()"

    *uniq_num = 0;
    *size = 0;

    return err;
}


void lmcal_cpu_port_set_vector_table(u32 base_addr, u32 offset)
{
   SCB->VTOR = (base_addr | offset) & CPU_PORT_MAX_ADDR;
}


//*********************************************** КОНЕЦ ФАЙЛА ***********************************************
