/*!
 * @file        system_apm32f4xx.c
 *
 * @brief       CMSIS Cortex-M4 Device Peripheral Access Layer System Source File
 *
 * @version     V1.0.5
 *
 * @date        2025-12-15
 *
 * @attention
 *
 *  Copyright (C) 2021-2025 Geehy Semiconductor
 *
 *  You may not use this file except in compliance with the
 *  GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
 *
 *  The program is only for reference, which is distributed in the hope
 *  that it will be useful and instructional for customers to develop
 *  their software. Unless required by applicable law or agreed to in
 *  writing, the program is distributed on an "AS IS" BASIS, WITHOUT
 *  ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the GEEHY SOFTWARE PACKAGE LICENSE for the governing permissions
 *  and limitations under the License.
 */

/* Includes ***************************************************************/
#include "apm32f4xx.h"
#include "system_apm32f4xx.h"

/* Private macro **********************************************************/

/* Uncomment the following line if you need to use external SRAM as data memory  */
/* #define DATA_IN_ExtSRAM */

/* Uncomment the following line if you need to relocate your vector Table in Internal SRAM. */
/* #define VECT_TAB_SRAM */

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

#define APB1_PSC 0x05
#define APB2_PSC 0x04

#if defined(APM32F405xx) || defined(APM32F407xx) || defined(APM32F415xx) || defined(APM32F417xx)
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_B) * PLL_A */
#define PLL_B      8U
/* USB OTG FS, SDIO and RNG Clock =  PLL_VCO / PLL_D */
#define PLL_D      7U
#define PLL_A      336U
/* SYSCLK = PLL_VCO / PLL_C */
#define PLL_C      2U

/* Select regulator voltage output Scale 1 mode */
#define REG_VOLTAGE_SCALE 0x01

/* Flash wait period */
#define FLASH_WAIT_PERIOD 0x05
#endif /* APM32F405xx || APM32F407xx || APM32F415xx || APM32F417xx */

#if defined(APM32F411xx)
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_B) * PLL_A */
#define PLL_B      8U
/* USB OTG FS, SDIO and RNG Clock =  PLL_VCO / PLL_D */
#define PLL_D      5U
#define PLL_A      240U
/* SYSCLK = PLL_VCO / PLL_C */
#define PLL_C      2U

/* Select regulator voltage output Scale 1 mode */
#define REG_VOLTAGE_SCALE 0x03

/* Flash wait period */
#define FLASH_WAIT_PERIOD 0x04
#endif /* APM32F411xx */

#if defined(APM32F425xx) || defined(APM32F427xx)
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_B) * PLL_A */
#define PLL_B      4U
/* USB OTG FS, SDIO and RNG Clock =  PLL_VCO / PLL_D */
#define PLL_D      10U
#define PLL_A      240U
/* SYSCLK = PLL_VCO / PLL_C */
#define PLL_C      2U

/* Select regulator voltage output Scale 1 mode */
#define REG_VOLTAGE_SCALE 0x01

/* Flash wait period */
#define FLASH_WAIT_PERIOD 0x07
#endif /* APM32F425xx || APM32F427xx */

#if defined(APM32F423xx)
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_B) * PLL_A */
#define PLL_B      4U
/* USB OTG FS, SDIO and RNG Clock =  PLL_VCO / PLL_D */
#define PLL_D      8U
#define PLL_A      192U
/* SYSCLK = PLL_VCO / PLL_C */
#define PLL_C      2U

/* Select regulator voltage output Scale 1 mode */
#define REG_VOLTAGE_SCALE 0x01

/* Flash wait period */
#define FLASH_WAIT_PERIOD 0x06
#endif /* APM32F423xx */

/* Private variables ******************************************************/

/**
 * @brief    APM32F4xx_System_Private_Variables
 */

uint32_t SystemCoreClock = HSI_VALUE;

__I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};

/* Private function prototypes ********************************************/

/**
 * @brief    APM32F4xx_System_Private_FunctionPrototypes
 */

static void SystemClockConfig(void);

#if defined(DATA_IN_ExtSRAM)
static void SystemInit_ExtSRAM(void);
#endif /* DATA_IN_ExtSRAM */

/* External functions *****************************************************/

/*!
 * @brief       Setup the microcontroller system
 *
 * @param       None
 *
 * @retval      None
 */
void SystemInit(void)
{
    /* FPU settings */
    #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  //!< set CP10 and CP11 Full Access
    #endif
    /* Reset the RCM clock configuration to the default reset state */
    /* Set HSIEN bit */
    RCM->CTRL_B.HSIEN = BIT_SET;

    /* Reset CFG register */
    RCM->CFG = 0x00000000;

    /* Reset HSEEN, CSSEN and PLL1EN bits */
    RCM->CTRL &= (uint32_t)0xFEF6FFFF;

    /* Reset PLL1CFG register */
    RCM->PLL1CFG = 0x24003010;

    /* Reset HSEBCFG bit */
    RCM->CTRL &= (uint32_t)0xFFFBFFFF;

    /* Disable all interrupts */
    RCM->INT = 0x00000000;

    #if defined(DATA_IN_ExtSRAM)
    SystemInit_ExtSRAM();
    #endif /* DATA_IN_ExtSRAM */

    SystemClockConfig();

    /* 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 = FMC_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
    #endif
}

/*!
 * @brief       Update SystemCoreClock variable according to Clock Register Values
 *              The SystemCoreClock variable contains the core clock (HCLK)
 *
 * @param       None
 *
 * @retval      None
 */
void SystemCoreClockUpdate(void)
{
    uint32_t sysClock, pllvco, pllc, pllClock, pllb;

    /* Get SYSCLK source */
    sysClock = RCM->CFG_B.SCLKSELSTS;

    switch (sysClock)
    {
        case 0:
            SystemCoreClock = HSI_VALUE;
            break;

        case 1:
            SystemCoreClock = HSE_VALUE;
            break;

        case 2:
            pllClock = RCM->PLL1CFG_B.PLLCLKS;
            pllb = RCM->PLL1CFG_B.PLLB;

            if (pllClock == 0)
            {
                /* HSI used as PLL clock source */
                pllvco = (HSI_VALUE / pllb) * (RCM->PLL1CFG_B.PLL1A); //!< PLL_VCO = (HSI_VALUE / PLL_B) * PLL_A */
            }
            else
            {
                /* HSE used as PLL clock source */
                pllvco = (HSE_VALUE / pllb) * (RCM->PLL1CFG_B.PLL1A); //!< PLL_VCO = (HSE_VALUE / PLL_B) * PLL_A */
            }

            pllc = ((RCM->PLL1CFG_B.PLL1C) + 1 ) *2;
            SystemCoreClock = pllvco/pllc;
            break;

        default:
            SystemCoreClock = HSI_VALUE;
            break;
    }

    /* Compute HCLK frequency */
    /* Get HCLK prescaler */
    sysClock = AHBPrescTable[(RCM->CFG_B.AHBPSC)];
    /* HCLK frequency */
    SystemCoreClock >>= sysClock;
}

/*!
 * @brief     Configures the System clock source, PLL Multiplier and Divider factors,
 *            AHB/APBx prescalers and Flash settings
 *
 * @param     None
 *
 * @retval    None
 */
static void SystemClockConfig(void)
{
    __IO uint32_t i;

    RCM->CTRL_B.HSEEN = BIT_SET;

    for (i = 0; i < HSE_STARTUP_TIMEOUT; i++)
    {
        if (RCM->CTRL_B.HSERDYFLG)
        {
            break;
        }
    }

    if (RCM->CTRL_B.HSERDYFLG)
    {
        /* Select regulator voltage output Scale 1 mode */
        RCM->APB1CLKEN_B.PMUEN = BIT_SET;
        PMU->CTRL_B.VOSSEL = REG_VOLTAGE_SCALE;

        /* HCLK = SYSCLK / 1*/
        RCM->CFG_B.AHBPSC = 0x0000;

        /* PCLK2 = HCLK / 2*/
        RCM->CFG_B.APB2PSC = APB2_PSC;

        /* PCLK1 = HCLK / 4*/
        RCM->CFG_B.APB1PSC = APB1_PSC;

        /* Configure the main PLL */
        RCM->PLL1CFG = PLL_B | (PLL_A << 6) | (((PLL_C >> 1) -1) << 16) |(PLL_D << 24);
        RCM->PLL1CFG_B.PLLCLKS = 0x01;

        /* Enable the main PLL */
        RCM->CTRL_B.PLL1EN = BIT_SET;

        /* Wait till the main PLL is ready */
        while (RCM->CTRL_B.PLL1RDYFLG == 0)
        {
        }

        /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
        FMC->ACCTRL = FLASH_WAIT_PERIOD | BIT8 | BIT9 | BIT10;

        /* Select the main PLL as system clock source */
        RCM->CFG_B.SCLKSEL = RESET;
        RCM->CFG_B.SCLKSEL = 0x02;

        /* Wait till the main PLL is used as system clock source */
        while ((RCM->CFG_B.SCLKSELSTS) != 0x02)
        {
        }
    }
    else
    {
        /* If HSE fails to start-up, the application will have wrong clock configuration. */
    }
    SystemCoreClockUpdate();
}

#if defined (DATA_IN_ExtSRAM)

/*!
 * @brief     Setup the external memory controller. Called in startup_apm32f4xx.s before jump to main.
 *            This function configures the external SRAM
 *            This SRAM will be used as program data memory (including heap and stack).
 *
 * @param     None
 *
 * @retval    None
 */
void SystemInit_ExtSRAM(void)
{
    /* Enable GPIOD, GPIOE, GPIOF and GPIOG interface clock */
    RCM->AHB1CLKEN |= 0x00000078;

    /* Connect PDx pins to FMC Alternate function */
    GPIOD->ALFL  = 0x00cc00cc;
    GPIOD->ALFH  = 0xcccccccc;
    /* Configure PDx pins in Alternate function mode */
    GPIOD->MODE  = 0xaaaa0a0a;
    /* Configure PDx pins speed to 100 MHz */
    GPIOD->OSSEL = 0xffff0f0f;
    /* Configure PDx pins Output type to push-pull */
    GPIOD->OMODE = 0x00000000;
    /* No pull-up, pull-down for PDx pins */
    GPIOD->PUPD  = 0x00000000;

    /* Connect PEx pins to FMC Alternate function */
    GPIOE->ALFL  = 0xcccccccc;
    GPIOE->ALFH  = 0xcccccccc;
    /* Configure PEx pins in Alternate function mode */
    GPIOE->MODE  = 0xaaaaaaaa;
    /* Configure PEx pins speed to 100 MHz */
    GPIOE->OSSEL = 0xffffffff;
    /* Configure PEx pins Output type to push-pull */
    GPIOE->OMODE = 0x00000000;
    /* No pull-up, pull-down for PEx pins */
    GPIOE->PUPD  = 0x00000000;

    /* Connect PFx pins to FMC Alternate function */
    GPIOF->ALFL  = 0x00cccccc;
    GPIOF->ALFH  = 0xcccc0000;
    /* Configure PFx pins in Alternate function mode */
    GPIOF->MODE  = 0xaa000aaa;
    /* Configure PFx pins speed to 100 MHz */
    GPIOF->OSSEL = 0xff000fff;
    /* Configure PFx pins Output type to push-pull */
    GPIOF->OMODE = 0x00000000;
    /* No pull-up, pull-down for PFx pins */
    GPIOF->PUPD  = 0x00000000;

    /* Connect PGx pins to FMC Alternate function */
    GPIOG->ALFL  = 0x00cccccc;
    GPIOG->ALFH  = 0x000000c0;
    /* Configure PGx pins in Alternate function mode */
    GPIOG->MODE  = 0x00080aaa;
    /* Configure PGx pins speed to 100 MHz */
    GPIOG->OSSEL = 0x000c0fff;
    /* Configure PGx pins Output type to push-pull */
    GPIOG->OMODE = 0x00000000;
    /* No pull-up, pull-down for PGx pins */
    GPIOG->PUPD  = 0x00000000;

    /* FMC Configuration */
    /* Enable the FMC/SMC interface clock */
    RCM->AHB3CLKEN |= 0x00000001;

    /* Configure and enable Bank1_SRAM2 */
    SMC_Bank1->CSCTRL2  = 0x00001011;
    SMC_Bank1->CSTIM2   = 0x00000201;
    SMC_Bank1E->WRTTIM2 = 0x0fffffff;
}
#endif /* DATA_IN_ExtSRAM */
