/**
	******************************************************************************
	* @file    system_hk32f030m.c
	* @author  laura.C
	* @version V1.0  
	* @brief   API file of system clk config
	* @changelist  
	******************************************************************************
*/ 

 /*
 This file configures the system clock as follows:
  *=============================================================================
  *                         Supported hk32f030m device
  *-----------------------------------------------------------------------------
  *        System Clock source                    | HSI32M
  *-----------------------------------------------------------------------------
  *        SYSCLK(Hz)                             | 32000000
  *-----------------------------------------------------------------------------
  *        HCLK(Hz)                               | 32000000
  *-----------------------------------------------------------------------------
  *        AHB Prescaler                          | 1
  *-----------------------------------------------------------------------------
  *        APB1 Prescaler                         | 1
  *-----------------------------------------------------------------------------
  *=============================================================================
  ******************************************************************************

  */
#include "hk32f030m.h"

/* system clock source */
#define SYSCLK_SRC_HSI8M		0x2
#define SYSCLK_SRC_HSI16M		0x3
#define SYSCLK_SRC_HSI32M		0x4
#define SYSCLK_SRC_LSI			0x5
#define SYSCLK_SCR_EXTCLK_IO	0x6

#define SYSCLK_SOURCE			SYSCLK_SRC_HSI32M

/* vector table location */
// #define VECT_TAB_SRAM
#ifndef VECT_TAB_OFFSET
#define VECT_TAB_OFFSET  0x0 /*!< Vector Table base offset field. This value must be a multiple of 0x200. */
#endif



uint32_t System_SysClk;			// SYSCLK which feeds AHB, ADC, USART, etc. System clock.
uint32_t System_HClk;			// HCLK which feeds core, AHB bus, memory.  CPU clock.

#if(SYSCLK_SOURCE==SYSCLK_SRC_HSI8M)
	#define SYSCLK_FREQ_HSI		  			32000000
	#define HCLK_FREQ						8000000
	#define SET_FLASH_LATENCY				FLASH_Latency_0
	#define SET_HPRE_DIV					RCC_CFGR_HPRE_DIV4
  	static void SetSysClockToHSI(void);

#elif(SYSCLK_SOURCE == SYSCLK_SRC_HSI16M)
	#define SYSCLK_FREQ_HSI		  			32000000
	#define HCLK_FREQ						16000000
	#define SET_FLASH_LATENCY				FLASH_Latency_0
	#define SET_HPRE_DIV					RCC_CFGR_HPRE_DIV2
  	static void SetSysClockToHSI(void);

#elif(SYSCLK_SOURCE == SYSCLK_SRC_HSI32M)
	#define SYSCLK_FREQ_HSI		  			32000000
	#define HCLK_FREQ						32000000
	#define SET_FLASH_LATENCY				FLASH_Latency_1
	#define SET_HPRE_DIV					RCC_CFGR_HPRE_DIV1
  	static void SetSysClockToHSI(void);

#elif(SYSCLK_SOURCE == SYSCLK_SRC_LSI)
	#define SYSCLK_FREQ_LSI    				LSI_VALUE
	uint32_t SystemCoreClock         = SYSCLK_FREQ_LSI;
	static void SetSysClockToLSI(void);	

#elif(SYSCLK_SOURCE == SYSCLK_SCR_EXTCLK_IO)
	#define SYSCLK_FREQ_EXTCLK	    		EXTCLK_VALUE
	uint32_t SystemCoreClock         = SYSCLK_FREQ_EXTCLK;
	static void SetSysClockToEXTCLK(void);	

#endif

static void SetSysClock(void);

/**
  * @brief  Setup the microcontroller system.
  *         Initialize the default HSI clock source, vector table location and the PLL configuration is reset.
  * @param  None
  * @retval None
  */
void SystemInit(void)
{
	/* Set HSION bit */
	RCC->CR |= (uint32_t)0x00000001;

	/* Reset SW[1:0], HPRE[3:0], PPRE[2:0] and MCOSEL[2:0] bits */
	RCC->CFGR &= (uint32_t)0xF8FFB81C;

	/* Reset USARTSW[1:0], I2CSW bits */
	RCC->CFGR3 &= (uint32_t)0xFFFFFFEC;

	/* Disable all interrupts */
	RCC->CIR = 0x00000000;

	SetSysClock();

#ifdef VECT_TAB_SRAM
	SYSCFG->CFGR1 |= SYSCFG_CFGR1_MEM_MODE; /* Vector Table Relocation in Internal SRAM. */
#else
  	// FLASH->INT_VEC_OFFSET = VECT_TAB_OFFSET ; /* Vector Table Relocation in Internal FLASH. */
#endif 	
}


/**
  * @brief  Configures the System clock frequency, HCLK, PCLK prescalers.
  * @param  None
  * @retval None
  */
static void SetSysClock(void)
{
  /*reload the  hsi trimming value to the bit3~bit13  of RCC_CR register */
  uint32_t u32HSIFLASH = 0;
  uint32_t u32RCC_CR = 0;
  uint32_t u32HSITemp = 0;
  uint16_t u16HSITempH = 0;
  uint16_t u16HSITempL = 0;  
  
  u32HSIFLASH = *(uint32_t *) 0x1FFFF820;
  u16HSITempH = (uint16_t)(u32HSIFLASH>>16);
  u16HSITempL = (uint16_t)(u32HSIFLASH);
 
  if(!(u16HSITempH & u16HSITempL))
  {
    u32HSITemp = RCC->CR;
    u32HSITemp &= (uint32_t)((uint32_t)~(RCC_CR_HSITRIM|RCC_CR_HSICAL));
    u32RCC_CR = (uint32_t)(((u16HSITempL & 0x001F) <<3) | (((u16HSITempL>>5) & 0x003F)<<8)); 
    RCC->CR |= u32RCC_CR;      
  } 
  /*end*/
#if(SYSCLK_SOURCE==SYSCLK_SRC_HSI8M)
	SetSysClockToHSI();
#elif(SYSCLK_SOURCE == SYSCLK_SRC_HSI16M)
	SetSysClockToHSI();
#elif(SYSCLK_SOURCE == SYSCLK_SRC_HSI32M)
	SetSysClockToHSI();
#elif(SYSCLK_SOURCE == SYSCLK_SRC_LSI)
	SetSysClockToLSI(); 
#elif(SYSCLK_SOURCE == SYSCLK_SCR_EXTCLK_IO)
	SetSysClockToEXTCLK();	
#endif
 
 /* If none of the define above is enabled, the HSI is used as System clock  source (default after reset) */ 
}

#ifdef SYSCLK_FREQ_HSI
static void SetSysClockToHSI(void)
{
	__IO uint32_t StartUpCounter = 0, HSIStatus = 0;
	__IO uint32_t ACRreg = 0;
	__IO uint32_t RCCHCLKReg = 0;	
	__IO uint32_t RCCPCLKReg = 0;		

	/* Enable HSI */
    RCC->CR |= RCC_CR_HSION;

    /* Set flash programming clock to 2MHz using HSI32M directly
     * Datasheet and RM was updated in 2023 which derated flash programming from 4MHz
     * 0x07 = 4MHz, 0x08 = 2MHz. change as you see fit */
	RCC->CFGR4 &= ~(RCC_RCC_CFGR4_FLITFCLK_PRE | RCC_RCC_CFGR4_FLITFCLK_SE);
	RCC->CFGR4 |= (((uint32_t)0x08) << RCC_RCC_CFGR4_FLITFCLK_PRE_Pos);

	/* Wait until HSI is ready; if timeout is reached, then exit */
	do {
		HSIStatus = RCC->CR & RCC_CR_HSIRDY;
		StartUpCounter++;  
	} while((HSIStatus == 0) && (StartUpCounter != HSI_STARTUP_TIMEOUT));

	if ((RCC->CR & RCC_CR_HSIRDY) != RESET) {
		HSIStatus = (uint32_t)0x01;
	} else {
		HSIStatus = (uint32_t)0x00;
	}  
	
	if (HSIStatus == (uint32_t)0x01) {
		/* Flash wait state */
		ACRreg = FLASH->ACR;
		ACRreg &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
		FLASH->ACR = (uint32_t)(SET_FLASH_LATENCY | ACRreg);

		/* HCLK = configured divided value from SYSCLK (which will later be set to HSI32M) */
		RCCHCLKReg = RCC->CFGR;
		RCCHCLKReg &= (uint32_t)((uint32_t)~RCC_CFGR_HPRE_Msk);
		RCC->CFGR = (uint32_t)(SET_HPRE_DIV | RCCHCLKReg);

		/* PCLK = HCLK */
		RCCPCLKReg = RCC->CFGR;
		RCCPCLKReg &= (uint32_t)((uint32_t)~RCC_CFGR_PPRE_Msk);
		RCC->CFGR = (uint32_t)(RCC_CFGR_PPRE_DIV1|RCCPCLKReg);

		/* Select HSI32M as system clock source */
		RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
		RCC->CFGR |= (uint32_t)RCC_CFGR_SW_HSI;    

		/* Wait until HSI is active as system clock source */
		while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI);
	} else {
		/* If clock fails to start, the application will have wrong clock configuration.
		 * User can add some code here to deal with this error
		 */
	}  
}

#elif (SYSCLK_SOURCE == SYSCLK_SRC_LSI)
static void SetSysClockToLSI(void)
{
	__IO uint32_t StartUpCounter = 0, LSIStatus = 0;
	
	/* Enable LSI */
    RCC->CSR |= RCC_CSR_LSION;

	/* Wait till LSI is ready and if Time out is reached exit */
	do{
		LSIStatus = RCC->CSR & RCC_CSR_LSIRDY;
		StartUpCounter++;  
	} while((LSIStatus == 0) && (StartUpCounter != STARTUP_TIMEOUT));

	if ((RCC->CSR & RCC_CSR_LSIRDY) != RESET)
	{
		LSIStatus = (uint32_t)0x01;
	}
	else
	{
		LSIStatus = (uint32_t)0x00;
	}  
	
	if (LSIStatus == (uint32_t)0x01)
	{

		/* Flash wait state */
		FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
		FLASH->ACR |= (uint32_t)FLASH_Latency_0;	

		/* HCLK = SYSCLK */
		RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;

		/* PCLK = HCLK */
		RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE_DIV1;

		/* Select HSI as system clock source */
		RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
		RCC->CFGR |= (uint32_t)RCC_CFGR_SW_LSI;    

		/* Wait till LSI is used as system clock source */
		while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != RCC_CFGR_SWS_LSI)
		{
		}

	}
	else
	{ /* If fails to start-up, the application will have wrong clock configuration. User can add here some code to deal with this error */
	}  	
}; 	

#elif (SYSCLK_SOURCE == SYSCLK_SCR_EXTCLK_IO)
static void SetSysClockToEXTCLK(void)
{
	__IO uint32_t StartUpCounter = 0, EXTCLKStatus = 0;
	__IO uint32_t ACRreg = 0;
	__IO uint32_t RCCHCLKReg = 0;	
	__IO uint32_t RCCPCLKReg = 0;
	//enable EXTIO PA1/PD7/PB5/PC5

	/* Configure PA1  as CLOCK input */
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_Init(GPIOA, &GPIO_InitStructure);


	// RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE);
	// GPIO_InitTypeDef GPIO_InitStructure;
	// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
	// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
	// GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	// GPIO_Init(GPIOD, &GPIO_InitStructure);


	// RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
	// GPIO_InitTypeDef GPIO_InitStructure;
	// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
	// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
	// GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	// GPIO_Init(GPIOB, &GPIO_InitStructure);

	// RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);
	// GPIO_InitTypeDef GPIO_InitStructure;
	// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
	// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
	// GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	// GPIO_Init(GPIOC, &GPIO_InitStructure);

	/*CLOCK select */
    RCC->CFGR4 &= (uint32_t)~(RCC_RCC_CFGR4_EXTCLK_SEL);
    RCC->CFGR4 |= (uint32_t)RCC_CFGR4_EXTCLK_SEL_PA1;  
	// RCC->CFGR4 |= (uint32_t)RCC_CFGR4_EXTCLK_SEL_PB5;  
	// RCC->CFGR4 |= (uint32_t)RCC_CFGR4_EXTCLK_SEL_PC5;  
	// RCC->CFGR4 |= (uint32_t)RCC_CFGR4_EXTCLK_SEL_PD7;  
	/* Enable EXTCLK */
    RCC->CR |= RCC_CR_EXTCLKON;

	/* Wait till LSI is ready and if Time out is reached exit */
	do{
		EXTCLKStatus = RCC->CR & RCC_CR_EXTCLKRDY;
		StartUpCounter++;  
	} while((EXTCLKStatus == 0) && (StartUpCounter != STARTUP_TIMEOUT));

	if ((RCC->CR & RCC_CR_EXTCLKRDY) != RESET)
	{
		EXTCLKStatus = (uint32_t)0x01;
	}
	else
	{
		EXTCLKStatus = (uint32_t)0x00;
	}  
	
	if (EXTCLKStatus == (uint32_t)0x01)
	{

		/* Flash wait state */
		ACRreg=	FLASH->ACR;
		ACRreg &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
		
		if (SystemCoreClock <= 16000000)
			FLASH->ACR = (uint32_t)(FLASH_Latency_0|ACRreg);
		else if(SystemCoreClock <= 32000000)
			FLASH->ACR = (uint32_t)(FLASH_Latency_1|ACRreg);
		else
			FLASH->ACR = (uint32_t)(FLASH_Latency_2|ACRreg);


		RCCHCLKReg = RCC->CFGR;
		RCCHCLKReg &= (uint32_t)((uint32_t)~RCC_CFGR_HPRE_Msk);
		/* HCLK = SYSCLK */
		RCC->CFGR = (uint32_t)(RCC_CFGR_HPRE_DIV1|RCCHCLKReg);

		RCCPCLKReg = RCC->CFGR;
		RCCPCLKReg &= (uint32_t)((uint32_t)~RCC_CFGR_PPRE_Msk);
		/* PCLK = HCLK */
		RCC->CFGR = (uint32_t)(RCC_CFGR_PPRE_DIV1|RCCPCLKReg);


		/* Select EXTCLK as system clock source */
		RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
		RCC->CFGR |= (uint32_t)RCC_CFGR_SW_EXTCLK;    

		/* Wait till EXTCLK is used as system clock source */
		while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != RCC_CFGR_SWS_EXTCLK)
		{
		}
	}
	else
	{ /* If fails to start-up, the application will have wrong clock configuration. User can add here some code to deal with this error */
	}  	
}; 	

#endif


/**
   * @brief  Update 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
  * @retval None
  */
void SystemCoreClockUpdate (void)
{
	uint32_t tmp = 0, presc = 0;

	/* Get SYSCLK source -------------------------------------------------------*/
	tmp = RCC->CFGR & RCC_CFGR_SWS;

	switch (tmp) {
		case RCC_CFGR_SWS_EXTCLK:		// EXTCLK used as system clock
			SystemCoreClock = EXTCLK_VALUE;
			break;
		case RCC_CFGR_SWS_LSI:			// LSI used as system clock
			SystemCoreClock = LSI_VALUE;
			break;

		case RCC_CFGR_SWS_HSI:			// HSI used as system clock
		default:
			SystemCoreClock = HCLK_FREQ;
			break;
	}
  
	/* Compute HCLK clock frequency ----------------*/
	/* Get HCLK prescaler */
	tmp = RCC->CFGR & RCC_CFGR_HPRE;
	tmp = tmp >> 4;
	presc = AHBPrescTable[tmp];
	/* HCLK clock frequency */
	SystemCoreClock = SystemCoreClock/presc;
}
