
/******************************************************************************
*@file  : system-acm32g103.c
*@brief : CMSIS Cortex-M33 Device Peripheral Access Layer System Source File
******************************************************************************/

#include "acm32g103_spl_conf.h"

typedef enum 
{
    SYSCLK_120M_SRC_RC64M = 0,
    SYSCLK_80M_SRC_RC64M,
    SYSCLK_64M_SRC_RC64M,
    SYSCLK_32M_SRC_RC64M,
    SYSCLK_16M_SRC_RC64M,
    SYSCLK_8M_SRC_RC64M,

    SYSCLK_120M_SRC_XTH_12M,
    SYSCLK_80M_SRC_XTH_12M,
    SYSCLK_60M_SRC_XTH_12M,
    SYSCLK_40M_SRC_XTH_12M,
    SYSCLK_24M_SRC_XTH_12M,
    SYSCLK_12M_SRC_XTH_12M

}SYSCLK_SelectTypeDef;

/****** system core clock select, uesr config ***********/
#define SYSCLK_SELECT       SYSCLK_120M_SRC_RC64M 

/******************************************************************************
*@brief : PCLK1_DIV_SELECT: pclk1 div select, uesr config
*           @arg RCC_PCLK1_DIV_1
*           @arg RCC_PCLK1_DIV_2
*           @arg RCC_PCLK1_DIV_4
*           @arg RCC_PCLK1_DIV_8
*           @arg RCC_PCLK1_DIV_16
******************************************************************************/
#define PCLK1_DIV_SELECT    RCC_PCLK1_DIV_2

/******************************************************************************
*@brief : PCLK2_DIV_SELECT: pclk2 div select, uesr config
*           @arg RCC_PCLK2_DIV_1
*           @arg RCC_PCLK2_DIV_2
*           @arg RCC_PCLK2_DIV_4
*           @arg RCC_PCLK2_DIV_8
*           @arg RCC_PCLK2_DIV_16
******************************************************************************/
#define PCLK2_DIV_SELECT    RCC_PCLK2_DIV_2

/* #define VECT_TAB_SRAM */
#define VECT_TAB_OFFSET  0x0U /*!< Vector Table base offset field. This value must be a multiple of 0x200. */

#ifdef DATA_IN_ExtSRAM
  static void SystemInit_ExtMemCtl(void); 
#endif

/******************************************************************************
*@note  : g_SystemCoreClock variable is updated in three ways:
*           1) by calling CMSIS function SystemCoreClockUpdate()
*           2) by calling HAL API function HAL_RCC_GetSysCoreClockFreq()
*           3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency 
*               If you use this function to configure the system clock; then there
*               is no need to call the 2 first functions listed above, since g_SystemCoreClock
*               variable is updated automatically.
******************************************************************************/
uint32_t g_SystemCoreClock = 64000000;


/******************************************************************************
*@brief : configure FPU and vector table address
*         - This function is called at startup just after reset and before branch to main program. 
*         - This call is made inside the "startup_acm32g103.s" file
*@param : none
*@return: none
******************************************************************************/
void SystemInit(void)
{
	#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
		/* set CP10 and CP11 Full Access */
		SCB->CPACR |= ((3UL << 10*2) | (3UL << 11*2));
	#endif
    
    RCC->PLLCR &= ~RCC_PLLCR_PLLEN;
    RCC->PLLCR |= RCC_PLLCR_PLLSLEEP;
    
	RCC->RC64MCR &= ~RCC_RC64MCR_RC64MDIV;
	RCC->RC64MCR |= RCC_RC64MCR_RC64MEN;
	
	while (!(RCC->RC64MCR & RCC_RC64MCR_RC64MRDY))
	{
	};
	while (!(RCC->RC64MCR & RCC_RC64MCR_RC64MRDY))
	{
	};
	
	RCC->CCR1 &= ~RCC_CCR1_SYSCLKSEL;	/* sysclk = RC64M  */
	 
	RCC->RC64MCR &= ~RCC_RC64MCR_RC64MDIV;
    
	RCC->CCR2 &= ~RCC_CCR2_SYSDIV0;
    
	while(!(RCC->CCR2 & RCC_CCR2_DIVDONE))
    {
    };
	while(!(RCC->CCR2 & RCC_CCR2_DIVDONE))
    {
    };
	
	RCC->CCR2 &= ~RCC_CCR2_SYSDIV1;
	
	while(!(RCC->CCR2 & RCC_CCR2_DIVDONE))
    {
    };
	while(!(RCC->CCR2 & RCC_CCR2_DIVDONE))
    {
    };
	
	RCC->CCR2 &= ~(RCC_CCR2_PCLK1DIV | RCC_CCR2_PCLK2DIV);
	
	
    RCC->XTHCR = 0;
    RCC->PLLCR = 0;
    RCC->STDBYCTRL &= ~RCC_STDBYCTRL_XTLEN;
    RCC->STDBYCTRL |= RCC_STDBYCTRL_RC32EN;
    
	#ifdef DATA_IN_ExtSRAM
		SystemInit_ExtMemCtl(); 
	#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */

    /* Configure the Vector Table location add offset address ------------------*/
	#ifdef VECT_TAB_SRAM
		SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET;    /* Vector Table Relocation in Internal SRAM */
	#else
		SCB->VTOR = EFLASH_BASE_ADDR | VECT_TAB_OFFSET;  /* Vector Table Relocation in Internal FLASH */
	#endif
}

/******************************************************************************
*@brief : Update g_SystemCoreClock variable according to Clock Register Values.
*         The SystemCoreClock variable contains the core clock (HCLK), it can
*         be used by the user application to setup the SysTick timer or configure
*         other parameters.
*           
*@note  : Each time the core clock (HCLK) changes, this function must be called
*         to update SystemCoreClock variable value. Otherwise, any configuration
*         based on this variable will be incorrect.  
*@param : none
*@return: none
******************************************************************************/
void SystemCoreClockUpdate(void)
{
    g_SystemCoreClock = RCC_GetHCLKFreq();   
}


/******************************************************************************
*@brief : fast config system core clock. 
*@param : sysclkSel: system core clk select, see SYSCLK_SelectTypeDef  enum
*@param : pclk1Div: pclk1 div select
*           @arg RCC_PCLK1_DIV_1
*           @arg RCC_PCLK1_DIV_2
*           @arg RCC_PCLK1_DIV_4
*           @arg RCC_PCLK1_DIV_8
*           @arg RCC_PCLK1_DIV_16
*@param : pclk2Div: pclk2 div select
*           @arg RCC_PCLK2_DIV_1
*           @arg RCC_PCLK2_DIV_2
*           @arg RCC_PCLK2_DIV_4
*           @arg RCC_PCLK2_DIV_8
*           @arg RCC_PCLK2_DIV_16
*@return: none
******************************************************************************/
void SystemClock_FastConfig(SYSCLK_SelectTypeDef sysclkSel, uint32_t pclk1Div, uint32_t pclk2Div)
{
    switch(sysclkSel)
    {
        case SYSCLK_120M_SRC_RC64M:
            
            RCC_RC64MConfig(RCC_RC64M_DIV16);
            RCC_RC64MCmd(ENABLE);
            if (RCC_WaitForRC64MStartUp() != SUCCESS)
            {
                while (1);
            }
            
            RCC_PLLCmd(ENABLE);
            RCC_PLLConfig(RCC_PLLSOURCE_RC64M_DIV16, 0, 0, 15);
            RCC_PLLUpdateCmd(ENABLE);
            if (RCC_WaitForPLLStartUp() != SUCCESS)
            {
                while (1);
            }
            
            RCC_SYSCLKConfig(RCC_SYSCLKSOURCE_PLLCLK);
            RCC_HCLKConfig(RCC_SYSDIV0_DIV_1, RCC_SYSDIV1_DIV_1);
            RCC_PCLK1Config(pclk1Div);
            RCC_PCLK2Config(pclk2Div);

            break;
        
        case SYSCLK_80M_SRC_RC64M:
            
            RCC_RC64MConfig(RCC_RC64M_DIV16);
            RCC_RC64MCmd(ENABLE);
            if (RCC_WaitForRC64MStartUp() != SUCCESS)
            {
                while (1);
            }
            
            RCC_PLLCmd(ENABLE);
            RCC_PLLConfig(RCC_PLLSOURCE_RC64M_DIV16, 0, 0, 5);
            RCC_PLLUpdateCmd(ENABLE);
            if (RCC_WaitForPLLStartUp() != SUCCESS)
            {
                while (1);
            }
            
            RCC_SYSCLKConfig(RCC_SYSCLKSOURCE_PLLCLK);
            RCC_HCLKConfig(RCC_SYSDIV0_DIV_1, RCC_SYSDIV1_DIV_1);
            RCC_PCLK1Config(pclk1Div);
            RCC_PCLK2Config(pclk2Div);

            break; 
            
        case SYSCLK_64M_SRC_RC64M:
        case SYSCLK_32M_SRC_RC64M:            
        case SYSCLK_16M_SRC_RC64M:        
        case SYSCLK_8M_SRC_RC64M:
            
            RCC_RC64MConfig(RCC_RC64M_DIV1);
            RCC_RC64MCmd(ENABLE);
            if (RCC_WaitForRC64MStartUp() != SUCCESS)
            {
                while (1);
            }
            
            RCC_SYSCLKConfig(RCC_SYSCLKSOURCE_RC64M);
            
            if(sysclkSel == SYSCLK_64M_SRC_RC64M)
            {
                RCC_HCLKConfig(RCC_SYSDIV0_DIV_1, RCC_SYSDIV1_DIV_1);
            }
            else if(sysclkSel == SYSCLK_32M_SRC_RC64M)
            {
                RCC_HCLKConfig(RCC_SYSDIV0_DIV_1, RCC_SYSDIV1_DIV_2);
            }
            else if(sysclkSel == SYSCLK_16M_SRC_RC64M)
            {
                RCC_HCLKConfig(RCC_SYSDIV0_DIV_1, RCC_SYSDIV1_DIV_4);
            }
            else if(sysclkSel == SYSCLK_8M_SRC_RC64M)
            {
                RCC_HCLKConfig(RCC_SYSDIV0_DIV_1, RCC_SYSDIV1_DIV_8);
            }
            else
            {
                RCC_HCLKConfig(RCC_SYSDIV0_DIV_1, RCC_SYSDIV1_DIV_1);
            }
            
            RCC_PCLK1Config(pclk1Div);
            RCC_PCLK2Config(pclk2Div);

            break;
            
        case SYSCLK_120M_SRC_XTH_12M:
            
            RCC_XTHConfig(RCC_XTH_ON);
            if (RCC_WaitForXTHStartUp() != SUCCESS)
            {
                while (1);
            }
        
            RCC_PLLCmd(ENABLE);
            RCC_PLLConfig(RCC_PLLSOURCE_XTH, 0, 2, 15);
            RCC_PLLUpdateCmd(ENABLE);
            if (RCC_WaitForPLLStartUp() != SUCCESS)
            {
                while (1);
            }
            
            RCC_SYSCLKConfig(RCC_SYSCLKSOURCE_PLLCLK);
            RCC_HCLKConfig(RCC_SYSDIV0_DIV_1, RCC_SYSDIV1_DIV_1);
            RCC_PCLK1Config(pclk1Div);
            RCC_PCLK2Config(pclk2Div);
            break; 

        case SYSCLK_80M_SRC_XTH_12M:
            
            RCC_XTHConfig(RCC_XTH_ON);
            if (RCC_WaitForXTHStartUp() != SUCCESS)
            {
                while (1);
            }
        
            RCC_PLLCmd(ENABLE);
            RCC_PLLConfig(RCC_PLLSOURCE_XTH, 0, 2, 5);
            RCC_PLLUpdateCmd(ENABLE);
            if (RCC_WaitForPLLStartUp() != SUCCESS)
            {
                while (1);
            }
            
            RCC_SYSCLKConfig(RCC_SYSCLKSOURCE_PLLCLK);
            RCC_HCLKConfig(RCC_SYSDIV0_DIV_1, RCC_SYSDIV1_DIV_1);
            RCC_PCLK1Config(pclk1Div);
            RCC_PCLK2Config(pclk2Div);
            break; 
        
        case SYSCLK_60M_SRC_XTH_12M:
            
            RCC_XTHConfig(RCC_XTH_ON);
            if (RCC_WaitForXTHStartUp() != SUCCESS)
            {
                while (1);
            }
        
            RCC_PLLCmd(ENABLE);
            RCC_PLLConfig(RCC_PLLSOURCE_XTH, 1, 2, 15);
            RCC_PLLUpdateCmd(ENABLE);
            if (RCC_WaitForPLLStartUp() != SUCCESS)
            {
                while (1);
            }
            
            RCC_SYSCLKConfig(RCC_SYSCLKSOURCE_PLLCLK);
            RCC_HCLKConfig(RCC_SYSDIV0_DIV_1, RCC_SYSDIV1_DIV_1);
            RCC_PCLK1Config(pclk1Div);
            RCC_PCLK2Config(pclk2Div);
            break;
        
        case SYSCLK_40M_SRC_XTH_12M:
            
            RCC_XTHConfig(RCC_XTH_ON);
            if (RCC_WaitForXTHStartUp() != SUCCESS)
            {
                while (1);
            }
        
            RCC_PLLCmd(ENABLE);
            RCC_PLLConfig(RCC_PLLSOURCE_XTH, 1, 2, 5);
            RCC_PLLUpdateCmd(ENABLE);
            if (RCC_WaitForPLLStartUp() != SUCCESS)
            {
                while (1);
            }
            
            RCC_SYSCLKConfig(RCC_SYSCLKSOURCE_PLLCLK);
            RCC_HCLKConfig(RCC_SYSDIV0_DIV_1, RCC_SYSDIV1_DIV_1);
            RCC_PCLK1Config(pclk1Div);
            RCC_PCLK2Config(pclk2Div);
            break;
            
        case SYSCLK_24M_SRC_XTH_12M:
            
            RCC_XTHConfig(RCC_XTH_ON);
            if (RCC_WaitForXTHStartUp() != SUCCESS)
            {
                while (1);
            }
        
            RCC_PLLCmd(ENABLE);
            RCC_PLLConfig(RCC_PLLSOURCE_XTH, 2, 2, 9);
            RCC_PLLUpdateCmd(ENABLE);
            if (RCC_WaitForPLLStartUp() != SUCCESS)
            {
                while (1);
            }
            
            RCC_SYSCLKConfig(RCC_SYSCLKSOURCE_PLLCLK);
            RCC_HCLKConfig(RCC_SYSDIV0_DIV_1, RCC_SYSDIV1_DIV_1);
            RCC_PCLK1Config(pclk1Div);
            RCC_PCLK2Config(pclk2Div);
            break;

        case SYSCLK_12M_SRC_XTH_12M:
            
            RCC_XTHConfig(RCC_XTH_ON);
            if (RCC_WaitForXTHStartUp() != SUCCESS)
            {
                while (1);
            }
            
            RCC_SYSCLKConfig(RCC_SYSCLKSOURCE_XTH);
            RCC_HCLKConfig(RCC_SYSDIV0_DIV_1, RCC_SYSDIV1_DIV_1);
            RCC_PCLK1Config(pclk1Div);
            RCC_PCLK2Config(pclk2Div);
            
        default:            
        
            return;        
    }
}



/******************************************************************************
*@brief : flexible config system clock
*@param : none
*@return: none
******************************************************************************/
void SystemClock_FlexibleConfig(void)
{
    RCC_RC64MConfig(RCC_RC64M_DIV16);
    RCC_RC64MCmd(ENABLE);
    if (RCC_WaitForRC64MStartUp() != SUCCESS)
    {
        while (1);
    }
    
    RCC_PLLCmd(ENABLE);
    RCC_PLLConfig(RCC_PLLSOURCE_RC64M_DIV16, 0, 0, 15);
    RCC_PLLUpdateCmd(ENABLE);
    if (RCC_WaitForPLLStartUp() != SUCCESS)
    {
        while (1);
    }
    
    RCC_SYSCLKConfig(RCC_SYSCLKSOURCE_PLLCLK);
    RCC_HCLKConfig(RCC_SYSDIV0_DIV_1, RCC_SYSDIV1_DIV_1);
    RCC_PCLK1Config(RCC_PCLK1_DIV_2);
    RCC_PCLK2Config(RCC_PCLK2_DIV_2);
}

/******************************************************************************
*@brief : config system clock, user call it in main()
*@param : none
*@return: none
******************************************************************************/
void SystemClock_Config(void)
{
    SystemClock_FastConfig(SYSCLK_SELECT,PCLK1_DIV_SELECT,PCLK2_DIV_SELECT);
    //SystemClock_FlexibleConfig();
}
  
#ifdef DATA_IN_ExtSRAM

static void SystemInit_ExtMemCtl(void)
{
}

#endif /* (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM) */

/*********************************************************************************
* Function    : System_USB_PHY_Config
* Description : Configure USB PHY, such as clock select, pll...
* Input       : none  
* Output      : 0: fail, 1:success 
* Author      : xwl                         Date : 2021Äê
**********************************************************************************/
void System_USB_PHY_Config(void) 
{
    volatile uint32_t delay_count; 

    SYSCFG->PHYCFG &= (~BIT2); // exit power down, auto select clock source 

    delay_count = 20000;  
    while(delay_count--)
    {
        if (SYSCFG->PHYCFG & (BIT19)) // clksel_end flag = 1  
        {
            break;  
        }
    }  
}