/***********************************************************************
 * Copyright (c)  2017 - 2022, Unicmicro Co.,Ltd .
 * All rights reserved.
 * Filename    : system_um321xF.c
 * Description : system config source file (for example: clock config.....)
 * Author(s)   : Dan
 * version     : V1.0
 * Modify date : 2022-07-10
 ***********************************************************************/
#include  "system_um321xF.h"
#include  "config.h"

uint32_t SystemCoreClock = 0;   //core时钟，HCLK  (uint:Hz)
uint32_t SRCClock = 48000000;   //源时钟,clk_src, (uint:Hz)
uint32_t PCLK = 0;     		    //APB时钟/PCLK,外设时钟 (uint:Hz)
uint32_t RCLClock = 32000;   	//RCL时钟,clk_src, (uint:Hz)

static uint8_t div_cal(uint8_t div)
{
	uint8_t ret;
	switch (div)   							
	{
		case 0x01: ret = 0; break;
		case 0x02: ret = 1; break;
		case 0x04: ret = 2; break;
		case 0x08: ret = 3; break;
		case 0x10: ret = 4; break;
		case 0x20: ret = 5; break;
		case 0x40: ret = 6; break;
		case 0x80: ret = 7; break;
		default	 : ret = 0; break;              //如果以上分频都没有，则设为48M
	}
	return ret;
}

/************************************************************************
 * function   : div_calculate
 * Description: clock div_calculate, calculate frequency division coefficient of rch and hclk
 * input : 
 * 		   uint32_t system_clk_hz: expected system core clock
 *		   uint8_t *rch_div
 *		   uint8_t *hclk_div
 * return: none
 ************************************************************************/
static void div_calculate(uint32_t system_clk_hz, uint8_t *rch_div, uint8_t *hclk_div)
{
	uint8_t i = 0, j = 0;
	uint16_t fre_div = 0;
	uint8_t rch_div_table[4] = {1,2,3,4}, hclk_div_table[8] = {1,2,4,8,16,32,64,128};
	
	if(SRCClock%system_clk_hz == 0)
		fre_div = SRCClock/system_clk_hz;
	else
		goto div_result;
	
	for(i = 0; i<4; i++)
	{
		for(j = 0; j<8; j++)
		{
			if(fre_div%rch_div_table[i] == 0)
			{
				if(hclk_div_table[j] == fre_div/rch_div_table[i])
				{
					goto div_result;
				}
			}
		}
	}
	/*若无法分频出system_clk_hz的时钟，默认系统时钟为48MHz*/
	i = 0;
	j = 0;
	
div_result:
	*rch_div = rch_div_table[i];
	*hclk_div = hclk_div_table[j];
}

/************************************************************************
 * function   : clock_init
 * Description: clock init, initil several clock variables
 * input : 
 *		   uint32_t system_clk_hz: expected system core clock
 *		   for example,you can set 24000000Hz、16000000Hz、12000000Hz、8000000Hz、6000000Hz、4000000Hz、3000000Hz、2000000Hz、1000000Hz、......、93750Hz
 *		   Only support system_clk_hz = 48000000/rch_div/hclk_div(rch_div = 1,2,3,4 ;hclk_div = 1,2,4,8,16,32,64,128)
 * return: none
 ************************************************************************/
void clock_init(uint32_t system_clk_hz)
{
	uint8_t wait_time=1;
	uint32_t temp,rch48m,rcl32k;
	
	uint8_t rch_div;
	uint8_t hclk_div;
	div_calculate(system_clk_hz, &rch_div, &hclk_div);//若输入的系统时钟值不合法（即分频器无法分出该时钟值），默认为48MHz
	if((rch_div == 1) && (hclk_div == 1))	system_clk_hz = SRCClock;
	
	
	//系统时钟频率
	rch48m = (*(volatile uint32_t *)(0x01010034));	//RCH实测值
	if ((rch48m <= 50000000) && (rch48m >= 46000000)) 
		SystemCoreClock = rch48m/(rch_div*hclk_div);
    else 
		SystemCoreClock = system_clk_hz;	
		
	PCLK = SystemCoreClock;	

	//RCL时钟频率
	rcl32k = (*(volatile uint32_t *)(0x01010038));
	rcl32k = rcl32k/100;
	if ((rcl32k <= 34000) && (rcl32k >= 30000)) RCLClock = rcl32k;
	
	
	/*系统时钟配置*/
	REG_SCU_CTRLPROTECT = 0xA5A55A5A;				 //清除SYSCTRL0的写保护
	temp = REG_SCU_CTRL0 & (~(3<<17));              
	temp &= ~(3<<9);
	REG_SCU_CTRL0 = temp|((rch_div-1)<<17)|CLK_DIV_APB;	//RCH时钟源和APB分频配置	

	hclk_div = div_cal(hclk_div);
	REG_SCU_CTRLPROTECT = 0xA5A55A5A;		 		//清除SYSCTRL0的写保护
	REG_SCU_CTRL0 = (REG_SCU_CTRL0&(~(7<<6)))|((hclk_div)<<6);	//HCLK分频配置
	
	REG_SCU_CTRLPROTECT = 0xA5A55A5A;		 		//清除SYSCTRL0的写保护
	REG_SCU_CTRL0 |= 1<<0;					 		//RCH时钟使能
	while((REG_SCU_OSCCTRL&0x400) == 0x00);  		//等待时钟稳定
	
	REG_SCU_CTRLPROTECT = 0xA5A55A5A;		 		//清除SYSCTRL0的写保护
	REG_SCU_CTRL0 &= ~(1<<14);				 		//系统时钟来源为系统高速时钟
	
	REG_SCU_CTRLPROTECT = 0xA5A55A5A;				//清除SYSCTRL0的写保护
	REG_SCU_CTRL0 &= ~(1<<13);				 		//高频时钟选择内部高速时钟RCH
	

	/*EFC配置*/
	if ((SystemCoreClock <= 50000000) && (SystemCoreClock >= 46000000)) 
    {
    	wait_time = 1;              //系统时钟为48M时，读等待时间最少为1
    }
    else wait_time = 0;	
	REG_EFC_CTRL = (REG_EFC_CTRL &(~(0x0f<<3))) | (wait_time << 3);	
	
}

/************************************************************************
 * function   : io_status_init
 * Description: io_status_init 除了boot通信串口引脚，其余引脚配置为输入上拉的GPIO功能
 * input : none
 * return: none
 ************************************************************************/
static void io_status_init(void)
{
	REG_SCU_IOCTRLPROTECT = 0xA5A55A5A; //清除IO写保护
	
	/*配置所有端口为复位默认功能*/
    REG_SCU_PASEL = 0x00000000;
	REG_SCU_PBSEL = 0x00000000;
    REG_SCU_PCSEL = 0x01100000;    
	REG_SCU_PDSEL = 0x00000000;
	REG_SCU_PESEL = 0x00000000;
	REG_SCU_PFSEL = 0x00000000;
	REG_SCU_PGSEL = 0x00000000;
	
	/*所有io上拉*/
	REG_SCU_PADPU0 = 0xFFFFFFFF;
	REG_SCU_PADPU1 = 0xFFFFFFFF;
    
    /*所有io输入使能*/
	REG_SCU_PADIE0 = 0xFFFFFFFF;
	REG_SCU_PADIE1 = 0xFFFFFFFF;
	  
    REG_SCU_IOCTRLPROTECT = 0xffffffff; //开启IO写保护
    while((REG_SCU_IOCTRLPROTECT & 0x01) == 0x01)      //读IO控制保护寄存器的写使能状态
    {
        REG_SCU_IOCTRLPROTECT = 0xffffffff; //开启IO写保护
    }	
	

}

/************************************************************************
 * function   : SystemInit
 * Description: SystemInit
 * input : none
 * return: none
 ************************************************************************/
void SystemInit(void)
{
	clock_init(FCLK);   //时钟初始化设置
	io_status_init();   //io状态初始化
}

/************************************************************************
 * function   : sys_entersleep
 * Description: enter sleep
 * input : none
 * return: none
 ************************************************************************/
void sys_entersleep(void)
{
	//Sleep
	SCB->SCR &= ~(1<<2);			    //sleep	
	//issue sleep command
  /*WFI*/  
	__WFI();	//enter sleep
    
	return;
}

/************************************************************************
 * function   : sys_enterdeepsleep
 * Description: enter deepsleep
 * input : none
 * return: none
 ************************************************************************/
void sys_enterdeepsleep(void)
{
	//Deep Sleep
	SCB->SCR |= (1<<2);					//deep sleep
	//issue deep sleep command
    /*WFI*/
	__WFI();	//enter deep sleep

	return;
}

/************************************************************************
 * function   : sys_enterstop
 * Description: enter stop
 * input : none
 * return: none
 ************************************************************************/
void sys_enterstop(void)
{
	
	REG_SCU_STOPMODESEL = 0xA5A50001;	//STOP停止模式有效	
	
	SCB->SCR |= (1<<2);	
    /*WFI*/	
	__WFI();	//enter stop

	return;
}
