/******************************************************************************
 * @file     system_SN32F400.c
 * @brief    CMSIS Cortex-M0 Device Peripheral Access Layer Source File
 *           for the SONIX SN32F400 Devices
 * @version  V1.0.1
 * @date     2023/12/12
 *
 * @note
 * Copyright (C) 2022-2024 ARM Limited. All rights reserved.
 *
 * @par
 * ARM Limited (ARM) is supplying this software for use with Cortex-M 
 * processor based microcontrollers.  This file can be freely distributed 
 * within development tools that are supporting such ARM based processors. 
 *
 * @par
 * THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
 * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
 * ARM SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR
 * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 *
 ******************************************************************************/


#include <stdint.h>
#include <SN32F400.h>



/*
//-------- <<< Use Configuration Wizard in Context Menu >>> ------------------
*/

/*--------------------- Clock Configuration ----------------------------------
//
//		<o0.0..2>  SYSCLKSEL (SYS0_CLKCFG)
//					<0=> IHRC 12M
//					<1=> ILRC
//					<2=> EHS X'TAL
//					<3=> ELS X'TAL
//					<4=> PLL
//					<5=> IHRC 48M
//	
//		<o1> EHS Source Frequency (MHz)
//			<10-25>
//
//<e2> PLL ENABLE
//	<h> PLL Control Register (SYS0_PLLCTRL)
//			<i> F_CLKOUT = F_VCO / P = (F_CLKIN / F * M) / P
//			<i> 4 MHz <= F_CLKIN <= 25 MHz
//			<i> 4 MHz <= F_CLKIN/F <= 16 MHz
//			<i> 96 MHz <= (F_CLKIN / F * M) <= 120 MHz
//		<o3> MSEL
//					<2-31>
//		<o4> PSEL
//					<0=> P = 2
//					<1=> P = 4
//		<o5> FSEL
//					<0=> Reserved
//					<1=> F = 1
//					<2=> F = 2
//					<3=> F = 3
//		<o6> PLL CLKIN Source selection
//					<0=> IHRC
//					<1=> EHS X'TAL
//	</h>
//</e>
//		<o7>  AHB Clock Prescaler Register  (SYS0_AHBCP)
//					<0=> SYSCLK/1
//					<1=> SYSCLK/2
//					<2=> SYSCLK/4
//					<3=> SYSCLK/8
//					<4=> SYSCLK/16
//					<5=> SYSCLK/32
//					<6=> SYSCLK/64
//					<7=> SYSCLK/128
//		<o8>  SYSCLK prescaler Register  (SYS0_AHBCP)
//					<0=> SYSCLK/1
//					<1=> SYSCLK/1.5
//		<o9>  CLKOUT selection
//					<0=> Disable
//					<1=> ILRC
//					<2=> ELS X'TAL
//					<4=> HCLK
//					<5=> IHRC
//					<6=> EHS X'TAL
//					<7=> PLL
//		<o10> CLKOUT Prescaler Register  (SYS1_APBCP1)
//					<0=> CLKOUT selection/1
//					<1=> CLKOUT selection/2
//					<2=> CLKOUT selection/4
//					<3=> CLKOUT selection/8
//					<4=> CLKOUT selection/16
//					<5=> CLKOUT selection/32
//					<6=> CLKOUT selection/64
//					<7=> CLKOUT selection/128
//		<o11> ANTIEFT Register  (SYS0_ANTIEFT)
//					<0=>  Disable
//					<9=>  Level 1
//					<10=> Level 2
//					<11=> Level 3
//					<12=> Level 4
//					<1=>  Level 5
//					<2=>  Level 6
//					<3=>  Level 7
//					<4=>  Level 8
*/


#define SYS0_CLKCFG_VAL		0
#define EHS_FREQ					16
#define PLL_ENABLE				0
#define PLL_MSEL					10
#define PLL_PSEL					0
#define PLL_FSEL					1
#define PLL_CLKIN					0
#define AHB_PRESCALAR 		0x0
#define AHB_1P5PRESCALAR 	0x0
#define CLKOUT_SEL_VAL 		0x0
#define CLKOUT_PRESCALAR 	0x7
#define ANTI_EFT				 	0x0

/*
//-------- <<< end of configuration section >>> ------------------------------
*/


/*----------------------------------------------------------------------------
  DEFINES
 *----------------------------------------------------------------------------*/
#define	IHRC			0
#define	ILRC			1
#define EHSXTAL		2
#define ELSXTAL		3
#define PLL				4
#define	IHRC_48M	5

/*----------------------------------------------------------------------------
  Define clocks
 *----------------------------------------------------------------------------*/
#define __IHRC_FREQ			(12000000UL)
#define __ILRC_FREQ			(32000UL)
#define __ELS_XTAL_FREQ	(32768UL)

#define SYS0_PLLCTRL_VAL	(PLL_ENABLE<<15) | (PLL_CLKIN<<12) | (PLL_FSEL<<8)	| (PLL_PSEL<<5) |  PLL_MSEL


/*----------------------------------------------------------------------------
  Clock Variable definitions
 *----------------------------------------------------------------------------*/
uint32_t SystemCoreClock;	/*!< System Clock Frequency (Core Clock)*/


/*----------------------------------------------------------------------------
  Clock functions
 *----------------------------------------------------------------------------*/
void SystemCoreClockUpdate (void)            /* Get Core Clock Frequency      */
{
	uint32_t AHB_prescaler;

	switch (SN_SYS0->CLKCFG_b.SYSCLKST)
	{
		case 0:		//IHRC
			if(SN_SYS0->ANBCTRL_b.IHRCFREQ == 0x0) // IHRC = IHRC48M / 4
				SystemCoreClock = __IHRC_FREQ;
			else																	 // IHRC = IHRC48M / 1
				SystemCoreClock = __IHRC_FREQ * 4;
			break;
		case 1:		//ILRC
			SystemCoreClock = __ILRC_FREQ;
			break;
		case 2:		//EHS X'TAL
			SystemCoreClock = EHS_FREQ * 1000000;
			break;
		case 3:		//ELS X'TAL
			SystemCoreClock = __ELS_XTAL_FREQ;
			break;	
		case 4: 	//PLL
			if (SN_SYS0->PLLCTRL_b.PLLCLKSEL == 0x0)	//IHRC as F_CLKIN
				SystemCoreClock = __IHRC_FREQ * PLL_MSEL / PLL_FSEL / (PLL_PSEL+1) / 2;
			else
				SystemCoreClock = EHS_FREQ * 1000000 * PLL_MSEL / PLL_FSEL / (PLL_PSEL+1) /2;
			break;
		default:
			break;
	}

	switch (SN_SYS0->AHBCP_b.AHBPRE)
	{
		case 0:	AHB_prescaler = 1;	break;
		case 1:	AHB_prescaler = 2;	break;
		case 2:	AHB_prescaler = 4;	break;
		case 3:	AHB_prescaler = 8;	break;
		case 4:	AHB_prescaler = 16;	break;
		case 5:	AHB_prescaler = 32;	break;
		case 6:	AHB_prescaler = 64;	break;
		case 7:	AHB_prescaler = 128;break;
		default: break;	
	}

	SystemCoreClock /= AHB_prescaler;
	
	if (SN_SYS0->AHBCP_b.DIV1P5 == 1)
		SystemCoreClock = SystemCoreClock*2/3;

	//;;;;;;;;; Need for SN32F400 Begin	;;;;;;;;;
	if (SystemCoreClock > 48000000)
		SN_FLASH->LPCTRL = 0x5AFA0029;
	else if (SystemCoreClock >= 24000000)
		SN_FLASH->LPCTRL = 0x5AFA0019;
	else
		SN_FLASH->LPCTRL = 0x5AFA0000;
	//;;;;;;;;; Need for SN32F400 End	;;;;;;;;;

	return;
}

/**
 * Initialize the system
 *
 * @param  none
 * @return none
 *
 * @brief  Setup the microcontroller system.
 *         Initialize the System.
 */
void SystemInit (void)
{
	#if (SYS0_CLKCFG_VAL == IHRC)			//IHRC
	SN_SYS0->ANBCTRL_b.IHRCFREQ = 0;
	SN_SYS0->CLKCFG = 0x0;
	while ((SN_SYS0->CLKCFG & 0x70) != 0x0);
	#endif

	#if (SYS0_CLKCFG_VAL == ILRC)			//ILRC
	SN_SYS0->CLKCFG = 0x1;
	while ((SN_SYS0->CLKCFG & 0x70) != 0x10);
	#endif

	#if (SYS0_CLKCFG_VAL == EHSXTAL)	//EHS XTAL
	#if (EHS_FREQ > 12)
	SN_SYS0->ANBCTRL_b.EHSFREQ = 1;
	SN_FLASH->LPCTRL = 0x5AFA0029;
	#else
	SN_SYS0->ANBCTRL_b.EHSFREQ = 0;
	#endif
	SN_SYS0->ANBCTRL_b.EHSEN = 1;
	while ((SN_SYS0->CSST & 0x10) != 0x10);
	SN_SYS0->CLKCFG = 0x2;
	while ((SN_SYS0->CLKCFG & 0x70) != 0x20);
	#endif

	#if (SYS0_CLKCFG_VAL == ELSXTAL)	//ELS XTAL
	SN_SYS0->ANBCTRL_b.ELSEN = 1;
	while((SN_SYS0->CSST & 0x4) != 0x4);
	SN_SYS0->CLKCFG = 0x3;
	while ((SN_SYS0->CLKCFG & 0x70) != 0x30);
	#endif

	#if (PLL_ENABLE == 1)
	SN_SYS0->PLLCTRL = SYS0_PLLCTRL_VAL;
	if (PLL_CLKIN == 0x1)	//EHS XTAL as F_CLKIN
	{
		//Enable EHS
		#if (EHS_FREQ > 12)
		SN_SYS0->ANBCTRL_b.EHSFREQ = 1;
		#else
		SN_SYS0->ANBCTRL_b.EHSFREQ = 0;
		#endif
		SN_SYS0->ANBCTRL_b.EHSEN = 1;
		while ((SN_SYS0->CSST & 0x10) != 0x10);
	}
	while ((SN_SYS0->CSST & 0x40) != 0x40);
	#if (SYS0_CLKCFG_VAL == PLL)		//PLL
	SN_FLASH->LPCTRL = 0x5AFA0029;
	SN_SYS0->CLKCFG = 0x4;
	while ((SN_SYS0->CLKCFG & 0x70) != 0x40);
	#endif
	#endif
	
	#if (SYS0_CLKCFG_VAL == IHRC_48M)			//IHRC 48M
	SN_FLASH->LPCTRL = 0x5AFA0029;
	SN_SYS0->ANBCTRL_b.IHRCFREQ = 1;
	SN_SYS0->CLKCFG = 0x0;
	while ((SN_SYS0->CLKCFG & 0x70) != 0x0);
	#endif
	
	SN_SYS0->AHBCP = AHB_PRESCALAR;
	SN_SYS0->AHBCP_b.DIV1P5 = AHB_1P5PRESCALAR;

	#if (CLKOUT_SEL_VAL > 0)			//CLKOUT
	SN_SYS1->AHBCLKEN_b.CLKOUTSEL = CLKOUT_SEL_VAL;
	SN_SYS1->APBCP1_b.CLKOUTPRE = CLKOUT_PRESCALAR;
	#endif
	
	SN_SYS0->ANTIEFT = ANTI_EFT;
}
