/**
  **********************************************************************************
  * @file    cpm_drv.c
  * @author  Product application department
  * @version V1.0
  * @date    2020.03.12
  * @brief   CPM模块DRV层驱动.
  * @note 这个文件为应用层提供了操作CPM的接口:
  *     - 系统时钟配置接口.
  *     - 获取系统时钟，IPS时钟接口.
  *     - 设置时钟分频接口.
  *
  @verbatim
  ===================================================================================
                                  ##### 更新 #####
  ===================================================================================
  [..] 更新时间:
  [..] 更新人:
  [..] 更新内容:
        1. nothing.
  @endverbatim
  */

#include "cpm_drv.h"
#include "ccm_drv.h"

/*** 常量定义 *******************************************************************/
/* global variables */

/*** 全局变量定义 ***************************************************************/
/*** volatile **********/
__IO uint32_t g_core_clk;
__IO uint32_t g_sys_clk;
__IO uint32_t g_ips_clk;
__IO uint32_t g_trim_clk;
__IO uint32_t g_efm_clk;
__IO uint32_t g_sdram_clk;

/*** 函数定义 *******************************************************************/
/**
 * @brief 8M系统时钟选择
 *
 * @param[in] @ref NONE
 * @return @ref NONE
 */
void DRV_CPM_SystemClkOSC8MSelect(void)
{
	_cpm_set_osc8m_clk_en;
	while(!_cpm_get_osc8m_stable_flag);
		
	_cpm_set_soc_clk_osc8m_en;
	while(!_cpm_get_osc8m_select_flag);
}

/**
 * @brief 120M系统时钟选择
 *
 * @param[in] @ref NONE
 * @return @ref NONE
 */
void DRV_CPM_SystemClkOSC400MSelect(void)
{
	_cpm_set_osc400m_clk_en;
	while(!_cpm_get_osc400m_stable_flag);
		
	_cpm_set_soc_clk_osc400m_en;
	while(!_cpm_get_osc400m_select_flag);
}

/**
 * @brief 240M系统时钟选择
 *
 * @param[in] @ref NONE
 * @return @ref NONE
 */
void DRV_CPM_SystemClkOSC480MSelect(void)
{
	_cpm_set_usbphy480m_clk_en;
	while(!_cpm_get_usbphy480m_stable_flag);
		
	_cpm_set_soc_clk_usbphy480m_en;
	while(!_cpm_get_usbphy480m_select_flag);
}

/**
 * @brief 外部晶振系统时钟选择
 *
 * @param[in] @ref NONE
 * @return @ref NONE
 */
void DRV_CPM_SystemClkOSCEXTSelect(void)
{
	_cpm_set_oscext_clk_en;
	while(!_cpm_get_oscext_stable_flag);
		
	_cpm_set_soc_clk_oscext_en;
	while(!_cpm_get_oscext_select_flag);
}

/**
 * @brief 系统测试模式key设置
 *
 * @param[in] @ref NONE
 * @return @ref NONE
 */
void DRV_CPM_VccCoreTestModeKeySet(void)
{
	uint32_t tmp = (_cpm_get_core_test_mode_value & 0x3FFFFFFF);
	
	_cpm_write_core_test_mode_value(tmp);
	_cpm_write_core_test_mode_value(tmp|0x40000000);
	_cpm_write_core_test_mode_value(tmp|0x80000000);
	_cpm_write_core_test_mode_value(tmp|0xC0000000);
}

/**
 * @brief 系统测试模式设置
 *
 * @param[in] val 系统测试模式设置值
 * @return @ref NONE
 */
void DRV_CPM_VccCoreTestModeSet(uint32_t val)
{
	DRV_CPM_VccCoreTestModeKeySet();
	_cpm_set_core_test_mode_value(val);
	_cpm_clr_core_test_mode_value(0xC0000000);
}

/**
 * @brief 系统电压Trim值设置
 *
 * @param[in] trim_value 系统电压Trim设置值
 * @return @ref NONE
 */
void DRV_CPM_SystemClkVrefTrim(uint32_t trim_value)
{
	DRV_CPM_VccCoreTestModeKeySet();
	
	_cpm_set_overwrite_vccgtrimr_trim_en;
	
	if(trim_value == CPM_VREF_TRIM_090)
	{
		_cpm_set_core_voltage_0V9_on;
	}
	else
  {
		_cpm_set_core_voltage_0V9_off;
		_cpm_set_bias2_res_trim_value(trim_value);
	}
}

/**
 * @brief 关闭模块时钟
 *
 * @param[in] module_index 被关闭的时钟索引
 * @return @ref NONE
 */
void DRV_CPM_ModuleClkOff(uint8_t module_index)
{
    if(module_index < 32)
    {
        _cpm_set_multicgtcr_clk_off(module_index);
    }
    else if(module_index >= 32 && module_index < 64)
    {
		    _cpm_set_syscgtcr_clk_off(module_index-32);
    }
		else if(module_index >= 64 && module_index < 96)
		{
			  _cpm_set_ahb3cgtcr_clk_off(module_index-64);
		}
    else if(module_index >= 96 && module_index < 128)
    {
        _cpm_set_arithcgtcr_clk_off(module_index-96);
    }
    else if(module_index >= 128 && module_index < 160)
    {
        _cpm_set_ipscgtcr_clk_off(module_index-128);
    }
}

/**
 * @brief 打开模块时钟
 *
 * @param[in] module_index 被打开的时钟索引
 * @return @ref NONE
 */
void DRV_CPM_ModuleClkOn(uint8_t module_index)
{
    if(module_index < 32)
    {
    	_cpm_set_multicgtcr_clk_on(module_index);
    }
    else if(module_index >= 32 && module_index < 64)
    {
    	_cpm_set_syscgtcr_clk_on(module_index-32);
    }
		else if(module_index >= 64 && module_index < 96)
		{
			  _cpm_set_ahb3cgtcr_clk_on(module_index-64);
		}
    else if(module_index >= 96 && module_index < 128)
    {
        _cpm_set_arithcgtcr_clk_on(module_index-96);
    }
    else if(module_index >= 128 && module_index < 160)
    {
        _cpm_set_ipscgtcr_clk_on(module_index-128);
    }
}

/**
 * @brief 获取系统时钟
 *
 * @param[in] NONE
 * @return @ref 系统时钟，单位HZ
 */
uint32_t DRV_CPM_GetCOREClHz(uint8_t osc500m_flg)
{
	uint32_t clk_freq;
	
	if(_cpm_chk_sys_clk_src(CPM_SYSCLK_OSC8M))
	{
		clk_freq = DRV_SYS_OSC_CLK_8M;
	}
	else if(_cpm_chk_sys_clk_src(CPM_SYSCLK_OSC400M))
	{
		if(osc500m_flg == TRUE)
		{
			clk_freq = DRV_SYS_OSC_CLK_500M;
		}
		else
		{
			clk_freq = DRV_SYS_OSC_CLK_400M;
		}
 
	}
	else if(_cpm_chk_sys_clk_src(CPM_SYSCLK_USBPHY480M))
	{
		clk_freq = DRV_SYS_OSC_CLK_480M;
	}
	else if(_cpm_chk_sys_clk_src(CPM_SYSCLK_OSCEXT) == CPM_SYSCLK_OSCEXT)
	{
		clk_freq = DRV_SYS_OSC_CLK_12M;;
	}
	else
	{
		clk_freq = 0;
	}
	
	return ((uint32_t)(clk_freq / (((_cpm_get_core_clk_value>>28) & 0x0f) + 1)));
}
uint32_t DRV_CPM_GetSYSClHz(void)
{
	return ((uint32_t)(g_core_clk / ((_cpm_get_sys_clk_value & 0xff) + 1)));
}

/**
 * @brief 获取IPS时钟
 *
 * @param[in] NONE
 * @return @ref IPS时钟，单位HZ
 */
uint32_t DRV_CPM_GetIPSClHz(void)
{
    uint32_t ips_clk;
	
    if(_cpm_chk_ips_clk_div_en)
    { 
			ips_clk = g_sys_clk/((_cpm_get_peripheral1_clk_div_value & 0x0F) + 1);
    }
		else
		{
			ips_clk = g_sys_clk;
		}
		
    return ips_clk;
}

uint32_t DRV_CPM_GetSDRAMClHz(void)
{
    uint32_t sdram_clk;
	  sdram_clk = g_sys_clk/(((_cpm_get_peripheral1_clk_div_value>>16) & 0x0F) + 1);
    return sdram_clk;
}
/**
 * @brief 获取EFM时钟
 *
 * @param[in] NONE
 * @return @ref EFM时钟，单位HZ。=系统时钟
 */
uint32_t DRV_CPM_GetEFMClHz(void)
{
	return g_sys_clk;
}

/**
 * @brief 系统时钟分频设置
 *
 * @param[in] div 系统时钟源分频系数，取值范围【0x00~0xff】
 * @return @ref NONE
 */
void DRV_CPM_SetSystemClkDiv(uint8_t div)
{
	//_cpm_set_core_clk_div(0);
	_cpm_set_sys_clk_div(div);
  _cpm_update_sys_clk_div;
}

void DRV_CPM_SetCoreClkDiv(uint8_t div)
{
	_cpm_set_core_clk_div(div);
  _cpm_update_sys_clk_div;
}
/**
 * @brief CLKOUT输出时钟源分频设置
 *
 * @param[in] div 系统时钟源分频系数，取值范围【0x00~0xff】
 * @return @ref NONE
 */
void DRV_CPM_SetClkoutClkDiv(uint8_t div)
{
	_cpm_set_clkout_clk_div_en;
	_cpm_set_clkout_clk_div(div);
  _cpm_update_sys_clk_div;
}

/**
 * @brief Trace时钟源分频设置
 *
 * @param[in] div 系统时钟源分频系数，取值范围【0x00~0xff】
 * @return @ref NONE
 */
void DRV_CPM_SetTraceClkDiv(uint8_t div)
{
	_cpm_set_trace_clk_div_en;
	_cpm_set_trace_clk_div(div);
  _cpm_update_sys_clk_div;
}

/**
 * @brief Arith时钟分频设置
 *
 * @param[in] div Arith时钟源分频系数，取值范围【0x00~0x0f】
 * @return @ref NONE
 */
void DRV_CPM_SetArithClkDiv(uint8_t div)
{
	_cpm_set_arith_clk_div_en;
	_cpm_set_peripheral_arith_clk_div(div);
  _cpm_update_peripheral_clk_div;
}

/**
 * @brief Ahb3时钟分频设置
 *
 * @param[in] div Ahb3时钟源分频系数，取值范围【0x00~0x0f】
 * @return @ref NONE
 */
void DRV_CPM_SetAhb3ClkDiv(uint8_t div)
{
	_cpm_set_ahb3_clk_div_en;
	_cpm_set_peripheral_ahb3_clk_div(div);
  _cpm_update_peripheral_clk_div;
}

/**
 * @brief IPS时钟分频设置
 *
 * @param[in] div IPS时钟源分频系数，取值范围【0x00~0x0f】
 * @return @ref NONE
 */
void DRV_CPM_SetIpsClkDiv(uint8_t div)
{
	_cpm_set_ips_clk_div_en;
	_cpm_set_peripheral_ips_clk_div(div);
  _cpm_update_peripheral_clk_div;
}

void DRV_CPM_SetSdramClkDiv(uint8_t div)
{
	_cpm_set_sdram_clk_div_en;
	_cpm_set_peripheral_sdram_clk_div(div);
  _cpm_update_peripheral_clk_div;
}
/**
 * @brief TC时钟分频设置
 *
 * @param[in] div TC时钟源分频系数，取值范围【0x00~0x0f】
 * @return @ref NONE
 */
void DRV_CPM_SetTcClkDiv(uint8_t div)
{
	_cpm_set_tc_clk_div_en;
	_cpm_set_peripheral_tc_clk_div(div);
  _cpm_update_peripheral_clk_div;
}

/**
 * @brief MESH时钟分频设置
 *
 * @param[in] div MESH时钟源分频系数，取值范围【0x00~0x0f】
 * @return @ref NONE
 */
void DRV_CPM_SetMeshClkDiv(uint8_t div)
{
	_cpm_set_mesh_clk_div_en;
	_cpm_set_peripheral_mesh_clk_div(div);
  _cpm_update_peripheral_clk_div;
}

/**
 * @brief ADC时钟分频设置
 *
 * @param[in] div ADC时钟源分频系数，取值范围【0x00~0x0f】
 * @return @ref NONE
 */
void DRV_CPM_SetAdcClkDiv(uint8_t div)
{
	_cpm_set_adc_clk_div_en;
	_cpm_set_peripheral_adc_clk_div(div);
  _cpm_update_peripheral_clk_div;
}

/**
 * @brief MCC_ADR时钟分频设置
 *
 * @param[in] div MCC_ADR时钟源分频系数，取值范围【0x00~0x07】
 * @return @ref NONE
 */
void DRV_CPM_SetMccAdrClkDiv(uint8_t div)
{
	_cpm_set_peripheral_mcc_adr_clk_div(div);
  _cpm_update_peripheral_clk_div;
}

/**
 * @brief MCC时钟分频设置
 *
 * @param[in] div MCC时钟源分频系数，取值范围【0x00~0x1f】
 * @return @ref NONE
 */
void DRV_CPM_SetMccClkDiv(uint8_t div)
{
	_cpm_set_mcc_clk_div_en;
	_cpm_set_peripheral_mcc_clk_div(div);
  _cpm_update_peripheral_clk_div;
}

/**
 * @brief SDRAM2LCD时钟分频设置
 *
 * @param[in] div SDRAM2LCD时钟源分频系数，取值范围【0x00~0x1f】
 * @return @ref NONE
 */
void DRV_CPM_SetSdram2lcdClkDiv(uint8_t div)
{
	_cpm_set_sdram2lcd_clk_div_en;
	_cpm_set_peripheral_sdram2lcd_clk_div(div);  
	_cpm_update_peripheral_clk_div;
}

/**
 * @brief SDRAM_SM时钟分频设置
 *
 * @param[in] div SDRAM_SM时钟源分频系数，取值范围【0x00~0x0f】
 * @return @ref NONE
 */
void DRV_CPM_SetSdram_smClkDiv(uint8_t div)
{
	_cpm_set_sdramsm_clk_div_en;
	_cpm_set_peripheral_sdram_sm_clk_div(div);
  	_cpm_update_peripheral_clk_div;
}


/**
 * @brief CLCD时钟分频设置
 *
 * @param[in] div CLCD时钟源分频系数，取值范围【0x00~0x0f】
 * @return @ref NONE
 */
void DRV_CPM_SetClcdClkDiv(uint8_t div)
{
	_cpm_set_clcd_clk_div_en;
	_cpm_set_peripheral_clcd_clk_div(div);
  	_cpm_update_peripheral_clk_div;
}

/**
 * @brief SD_HOST时钟分频设置
 *
 * @param[in] div SD_HOST时钟源分频系数，取值范围【0x00~0x0f】
 * @return @ref NONE
 */
void DRV_CPM_SetSd_hostClkDiv(uint8_t div)
{
	_cpm_set_sdhost_clk_div_en;
	_cpm_set_peripheral_sdhost_clk_div(div);
  _cpm_update_peripheral_clk_div;
}

/**
 * @brief DCMI_SENSOR时钟分频设置
 *
 * @param[in] div DCMI_SENSOR时钟源分频系数，取值范围【0x00~0x0f】
 * @return @ref NONE
 */
void DRV_CPM_SetDcmi_sensorClkDiv(uint8_t div)
{
	_cpm_set_dcmisensor_clk_div_en;
	_cpm_set_peripheral_dcmi_sensor_clk_div(div);
  	_cpm_update_peripheral_clk_div;
}

/**
 * @brief DCMI_PIX时钟分频设置
 *
 * @param[in] div DCMI_PIX时钟源分频系数，取值范围【0x00~0x0f】
 * @return @ref NONE
 */
void DRV_CPM_SetDcmi_pixClkDiv(uint8_t div)
{
	_cpm_set_dcmipix_clk_div_en;
	_cpm_set_peripheral_dcmi_pix_clk_div(div);
  	_cpm_update_peripheral_clk_div;
}

/**
 * @brief MIPI_SAMPLE时钟分频设置
 *
 * @param[in] div MIPI_SAMPLE时钟源分频系数，取值范围【0x00~0x07】
 * @return @ref NONE
 */
void DRV_CPM_SetMipi_sampleClkDiv(uint8_t div)
{
	_cpm_set_mipisample_clk_div_en;
	_cpm_set_peripheral_mipi_sample_clk_div(div);
  	_cpm_update_peripheral_clk_div;
}

/**
 * @brief DMA2D_SRAM时钟分频设置
 *
 * @param[in] div DMA2D_SRAM时钟源分频系数，取值范围【0x00~0x1f】
 * @return @ref NONE
 */
void DRV_CPM_SetDma2d_sramClkDiv(uint8_t div)
{
	_cpm_set_dma2dsram_clk_div_en;
	_cpm_set_peripheral_dma2d_sram_clk_div(div) ;
  	_cpm_update_peripheral_clk_div;
}

/**
 * @brief USBC PHY时钟初始化设置
 *
 * @param[in] src_type
 *- CPM_USBPHY_INTER_OSC,
 *- CPM_USBPHY_EXTER_OSC,
 * @return @ref NONE
 */
void DRV_CPM_UsbPhyInit(uint8_t src_type)
{
	if(src_type == CPM_USBPHY_EXTER_OSC)
	{
		_cpm_set_oscext_clk_stable_time_value(0x3000);
		_cpm_set_oscext_clk_en;
	}
	
	//Enable PHY Power Switch
	_cpm_set_usbphy_power_switch_en;

	//delay at least 10ms, sys clk is 120MHz, in release obj, one clock_cycle is 6 clock
	//so delay(x) = (6 * x / 120)us = 0.05x us, we use 11ms here.so 11ms = (6 * x / 120)=>x=220000	
	DelayMS(11);
	
	//Enable PHY Regulator
	CCM->PHYPA &= ~0xFF;

	//delay at least 10us
	delay(300);

	_cpm_set_usbphy_input_isolation_dis;
	_cpm_set_usbphy_output_isolation_dis;
	
	if(src_type == CPM_USBPHY_EXTER_OSC)
	{
		while(!_cpm_get_oscext_stable_flag);
	}
	
	//delay at least 10ms
	DelayMS(10);
	_cpm_set_usbphy_conf_software_reset_dis;
	
	//delay at least 1ms
	DelayMS(1);
	_cpm_set_usbphy_pll_software_reset_dis;
	
	//delay at least 1ms
	DelayMS(1);
	_cpm_set_usbphy_reset_signal_mask_dis;

	//delay at least 1ms
	DelayMS(1);

	_ccm_en_phy_avalid(CCM);

	_ccm_en_phy_vbusvalid(CCM);

	_ccm_select_valid_dir_reg(CCM);
}

void DRV_CPM_PorReset(void)
{
	DRV_CPM_VccCoreTestModeKeySet();
	_cpm_soft_por_reset;
}



/**
 * @brief MCC时钟分频设置
 *
 * @param[in] div MCC时钟源分频系数，取值范围【0x00~0x1f】
 * @return @ref NONE
 */
void DRV_CPM_SetSDHostClkDiv(uint8_t div)
{
	_cpm_set_sdhost_clk_div_en;
	_cpm_set_peripheral_sdhost_clk_div(div);
  _cpm_update_peripheral_clk_div;
}





