/***********************************************************************************************************
 * Copyright (c)  2020 - 2021, Unicmicro Co.,Ltd .
 * All rights reserved.
 * Filename    : system_um321xA.c
 * Description : system config source file (for example: clock config.....)
 * Author(s)   : yanhaihua
 * version     : V1.0
 * Modify date : 2021-09-23
 ***********************************************************************************************************/
#include "system_um321xA.h"

#define SYSTEM_CLOCK_SRC_RCH						64000000												/* 系统内部高速时钟RCH */					
#define SYSTEM_CLOCK_SRC_RCL						32000													/* 系统内部低速时钟RCL */
#define SYSTEM_CLOCK_SRC_XTH						16000000												/* 系统外部高速时钟XTH,根据输入修改 */
#define SYSTEM_CLOCK_SRC_XTL						32000													/* 系统外部低速时钟XTL,根据输入修改 */
#define SYSTEM_CLOCK_RCH_TRIM						(*(volatile unsigned int *)(0x01010034))
#define SYSTEM_CLOCK_RCL_TRIM						(*(volatile unsigned int *)(0x01010038))

#define SYSTEM_CLOCK_SRC_RCH_EN						((uint32_t)(1<<0))										/* 使能内部高速时钟RCH */
#define SYSTEM_CLOCK_SRC_XTH_EN						((uint32_t)(1<<1))										/* 使能外部高速晶振XTH */
#define SYSTEM_CLOCK_SRC_RCL_EN						((uint32_t)(1<<2))										/* 使能内部低速时钟RCL */
#define SYSTEM_CLOCK_SRC_XTL_EN						((uint32_t)(1<<3))										/* 使能外部低速晶振XTL */

#define SYSTEM_CLOCK_SEL_MASK						((uint32_t)(1<<14))										/* 时钟来源选择Mask */
#define SYSTEM_CLOCK_SEL_HF							((uint32_t)~(1<<14))									/* 时钟来源选择：高速时钟 CLK_SEL_HF */
#define SYSTEM_CLOCK_SEL_LF							((uint32_t)(1<<14))										/* 时钟来源选择：低速时钟 CLK_SEL_LF */

#define SYSTEM_CLOCK_SEL_HF_MASK					((uint32_t)(1<<13))										/* 高速时钟选择Mask */
#define SYSTEM_CLOCK_SEL_HF_RCH						((uint32_t)~(1<<13))									/* 0：高频时钟选择内部高速时钟 RCH */
#define SYSTEM_CLOCK_SEL_HF_XTH						((uint32_t)(1<<13))										/* 1：高频时钟选择外部高速时钟 XTH */

#define SYSTEM_CLOCK_SEL_LF_MASK					((uint32_t)(1<<12))										/* 低速时钟选择Mask */
#define SYSTEM_CLOCK_SEL_LF_RCL						((uint32_t)~(1<<12))									/* 0：低频时钟选择内部低速时钟 RCL */
#define SYSTEM_CLOCK_SEL_LF_XTL						((uint32_t)(1<<12))										/* 1：低频时钟选择外部低速时钟 XTL */

#define SYSTEM_CLOCK_RCH_DIV_MASK					((uint32_t)(3<<17))										/* RCH分频mask */
#define SYSTEM_CLOCK_RCH_DIV2						((uint32_t)(1<<17))										/* RCH 2分频 */
#define SYSTEM_CLOCK_RCH_DIV3						((uint32_t)(2<<17))										/* RCH 3分频 */
#define SYSTEM_CLOCK_RCH_DIV4						((uint32_t)(3<<17))										/* RCH 4分频 */

#define SYSTEM_CLOCK_PCLK_DIV_MASK					((uint32_t)(3<<9))										/* PCLK分频mask */
#define SYSTEM_CLOCK_PCLK_DIV1						((uint32_t)(0<<9))										/* PCLK分频HCLK */
#define SYSTEM_CLOCK_PCLK_DIV2						((uint32_t)(1<<9))										/* PCLK分频HCLK/2 */
#define SYSTEM_CLOCK_PCLK_DIV4						((uint32_t)(2<<9))										/* PCLK分频HCLK/4 */
#define SYSTEM_CLOCK_PCLK_DIV8						((uint32_t)(3<<9))										/* PCLK分频HCLK/8 */

#define SYSTEM_CLOCK_HCLK_DIV_MASK					((uint32_t)(7<<6))										/* HCLK分频Mask */
#define SYSTEM_CLOCK_HCLK_DIV1						((uint32_t)(0<<6))										/* HCLK分频SystemClk */
#define SYSTEM_CLOCK_HCLK_DIV2						((uint32_t)(1<<6))										/* HCLK分频SystemClk/2 */
#define SYSTEM_CLOCK_HCLK_DIV4						((uint32_t)(2<<6))										/* HCLK分频SystemClk/4 */
#define SYSTEM_CLOCK_HCLK_DIV8						((uint32_t)(3<<6))										/* HCLK分频SystemClk/8 */
#define SYSTEM_CLOCK_HCLK_DIV16						((uint32_t)(4<<6))										/* HCLK分频SystemClk/16 */
#define SYSTEM_CLOCK_HCLK_DIV32						((uint32_t)(5<<6))										/* HCLK分频SystemClk/32 */
#define SYSTEM_CLOCK_HCLK_DIV64						((uint32_t)(6<<6))										/* HCLK分频SystemClk/64 */
#define SYSTEM_CLOCK_HCLK_DIV128					((uint32_t)(7<<6))										/* HCLK分频SystemClk/128 */


typedef enum
{
	CLOCK_SRC_RCH,
	CLOCK_SRC_RCL,
	CLOCK_SRC_XTH,
	CLOCK_SRC_XTL,
}em_clock_src_t;

void system_clock_src_init(em_clock_src_t clock_src);														/* 时钟源选择 */
void system_clock_init(uint32_t hclk_div,uint32_t pclk_div);												/* 时钟初始化 */

/***********************************************************************************************************
 * Function		: SystemInit
 * Description	: 系统初始化
 * Input		: none
 * Output		: none
 * Return		: none
 ***********************************************************************************************************/
void SystemInit(void)
{
	system_clock_init(SYSTEM_CLOCK_HCLK_DIV1,SYSTEM_CLOCK_PCLK_DIV1);

	/*EFC配置*/
	uint8_t wait_time = 0;
	REG_SCU_PERICLKEN |= (1<<20);
	REG_EFC_CTRL = (REG_EFC_CTRL &(~(0x0f<<3))) | (wait_time << 3);
	
	system_io_init();
}

/***********************************************************************************************************
 * Function		: system_io_init
 * Description	: 系统所有IO初始状态
 * Input		: none
 * Output		: none
 * Return		: none
 ***********************************************************************************************************/
void system_io_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;
	
	/* 所有io上拉 */
	REG_SCU_PADPU0 = 0xFFFFFFFF;
	REG_SCU_PADPU1 = 0xFFFFFFFF;
	
	REG_GPIO_CLR(GPIOB) |= (1<<0);																			/* 为了使得蜂鸣器不响 */
	
	/* 所有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		: system_enter_sleep
 * Description	: 系统进入休眠
 * Input		: none
 * Output		: none
 * Return		: none
 ***********************************************************************************************************/
void system_enter_sleep(void)
{

	SCB->SCR &= ~(1<<2);
    __WFE();
    __WFE();
}

/***********************************************************************************************************
 * Function		: system_enter_deepsleep
 * Description	: 系统进入深度休眠
 * Input		: none
 * Output		: none
 * Return		: none
 ***********************************************************************************************************/
void system_enter_deepsleep(void)
{
	SCB->SCR |= (1<<2);
    __WFE();
    __WFE();     
	__NOP();
	__NOP();
	__NOP();
	__NOP();
	__NOP();
}

/***********************************************************************************************************
 * Function		: system_enter_stop
 * Description	: 系统进入停止模式
 * Input		: none
 * Output		: none
 * Return		: none
 ***********************************************************************************************************/
void system_enter_stop(void)
{
	REG_SCU_STOPMODESEL = 0xA5A50001;																		/* STOP停止模式有效	*/
	SCB->SCR |= (1<<2);	
    __WFE();
    __WFE();
}

/***********************************************************************************************************
 * Function		: system_reboot
 * Description	: 软复位
 * Input		: none
 * Output		: none
 * Return		: none
 ***********************************************************************************************************/
void system_reboot(void)
{
	REG_SCU_REMAP &= ~(1<<0);
}

/***********************************************************************************************************
 * Function		: system_get_clock
 * Description	: 获取系统时钟
 * Input		: none
 * Output		: none
 * Return		: none
 ***********************************************************************************************************/
void system_get_clock(s_system_clock_t *system_clock)
{
	uint32_t temp_clk = 0;
	uint32_t temp = 0;
	
	temp_clk = REG_SCU_CTRL0;
	if((temp_clk & SYSTEM_CLOCK_SEL_LF) == SYSTEM_CLOCK_SEL_LF)												/* 低速时钟 */
	{
		if((temp_clk & SYSTEM_CLOCK_SEL_LF_XTL) == SYSTEM_CLOCK_SEL_LF_XTL)									/* 外部低速 */
		{
			system_clock->sysclk = SYSTEM_CLOCK_SRC_XTL;
		}
		else																								/* 内部低速 */
		{
			uint32_t rcl = SYSTEM_CLOCK_RCL_TRIM;
			if((rcl <= 34000) && (rcl >= 30000))
			{
				system_clock->sysclk = rcl;
				
			}
			else
			{
				system_clock->sysclk = SYSTEM_CLOCK_SRC_RCL;
			}
		}
	}
	else																									/* 高速时钟 */
	{
		if((temp_clk & SYSTEM_CLOCK_SEL_HF_XTH) == SYSTEM_CLOCK_SEL_HF_XTH)									/* 外部高速 */
		{
			system_clock->sysclk = SYSTEM_CLOCK_SRC_XTH;
		}
		else																								/* 内部高速 */
		{
			temp = REG_SCU_CTRL0 & SYSTEM_CLOCK_RCH_DIV_MASK;
			temp = temp>>17;																				/* 计算RCH分频 */
			
			if((SYSTEM_CLOCK_RCH_TRIM <= 34000000) && (SYSTEM_CLOCK_RCH_TRIM >= 30000000))
			{	
				system_clock->sysclk = SYSTEM_CLOCK_RCH_TRIM/(temp);
			}
			else 
			{
				system_clock->sysclk = SYSTEM_CLOCK_SRC_RCH/(temp+1);
			}
		}
	}

	temp = REG_SCU_CTRL0 & (SYSTEM_CLOCK_HCLK_DIV_MASK);
	temp = temp>>6;																							/* 计算HCLK分频 */
	system_clock->hclk = system_clock->sysclk>>temp;
	
	temp = REG_SCU_CTRL0 & (SYSTEM_CLOCK_PCLK_DIV_MASK);
	temp = temp>>9;																							/* 计算PCLK分频 */
	system_clock->pclk = system_clock->hclk>>temp;
}

/***********************************************************************************************************
 * Function		: system_clock_src_init
 * Description	: 系统时钟源选择
 * Input		: none
 * Output		: none
 * Return		: none
 ***********************************************************************************************************/
void system_clock_src_init(em_clock_src_t clock_src)
{
	switch(clock_src)
	{
		case CLOCK_SRC_RCH:
		{
			
			REG_SCU_CTRLPROTECT = 0xA5A55A5A;				 												/* 清除SYSCTRL0的写保护 */
			REG_SCU_CTRL0 = (REG_SCU_CTRL0 & (~(3<<17))) | SYSTEM_CLOCK_RCH_DIV2;							/* 设置RCH分频为2分频 */
			
			REG_SCU_CTRLPROTECT = 0xA5A55A5A;		 														/* 清除SYSCTRL0的写保护 */
			REG_SCU_CTRL0 |= SYSTEM_CLOCK_SRC_RCH_EN;					 									/* RCH时钟使能 */
			while((REG_SCU_OSCCTRL&0x400) == 0x00);  														/* 等待时钟稳定 */
			
			REG_SCU_CTRLPROTECT = 0xA5A55A5A;		 														/* 清除SYSCTRL0的写保护 */
			REG_SCU_CTRL0 &= SYSTEM_CLOCK_SEL_HF;															/* 系统时钟来源为系统高速时钟 */
															
			REG_SCU_CTRLPROTECT = 0xA5A55A5A;																/* 清除SYSCTRL0的写保护 */
			REG_SCU_CTRL0 &= SYSTEM_CLOCK_SEL_HF_RCH;				 										/* 高频时钟选择内部高速时钟RCH */
			
			break;
		}
		case CLOCK_SRC_RCL:
		{
			REG_SCU_CTRLPROTECT = 0xA5A55A5A;																/* 去除写保护 */
			REG_SCU_CTRL0 &= SYSTEM_CLOCK_SEL_LF;															/* 选择低速时钟 */
																	
			REG_SCU_CTRLPROTECT = 0xA5A55A5A;																/* 去除写保护 */
			REG_SCU_CTRL0 &= SYSTEM_CLOCK_SEL_LF_RCL;														/* 选择内部低速时钟 */
			
			REG_SCU_CTRLPROTECT = 0xA5A55A5A;																/* 去除写保护 */
			REG_SCU_CTRL0 |= SYSTEM_CLOCK_SRC_RCL_EN;														/* 使能RCH时钟 */
			while((REG_SCU_OSCCTRL & 0x04) == 0x00);  														/* 等待时钟稳定 */	
			break;
		}
		case CLOCK_SRC_XTH:
		{
			break;
		}
		case CLOCK_SRC_XTL:
		{
			break;
		}
	}
}

/***********************************************************************************************************
 * Function		: system_clock_init
 * Description	: 系统时钟初始化
 * Input		: none
 * Output		: none
 * Return		: none
 ***********************************************************************************************************/
void system_clock_init(uint32_t hclk_div,uint32_t pclk_div)
{
	uint32_t temp = REG_SCU_CTRL0;
	temp &= ~SYSTEM_CLOCK_PCLK_DIV_MASK;
	temp &= ~SYSTEM_CLOCK_HCLK_DIV_MASK;

	REG_SCU_CTRLPROTECT = 0xA5A55A5A;		 																/* 清除SYSCTRL0的写保护 */
	REG_SCU_CTRL0 = temp |hclk_div | pclk_div;																/* HCLK分频配置 和PCLK分频 */

	system_clock_src_init(CLOCK_SRC_RCH);
}
