/**************************************************************************//**
 * @file     system_pan271x.c
 * @version  V1.00
 * @brief    Panchip series SoC system clock init code and assert handler
 * @note
 * Copyright (C) 2025 Panchip Technology Corp. All rights reserved.
 *****************************************************************************/
#include "pan271x.h"
#include "pan_clk.h"
#include "pan_otp.h"
#include "pan_sys.h"

/*
 * Global variables defined here should be used after RAM RW/ZI data init.
 */
uint32_t SystemCoreClock;
bool isFtDataValid = true;

/**
  * @brief  Setup the microcontroller system
  *         Initialize the Embedded Flash Interface and other modules which should
  *         be init in early boot stage.
  * @param  None
  * @retval None
  */
void SystemInit(void)
{
//	PMU->CPU_ADDR_REMAP_CTRL = 0;
}

/**
  * @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 div = 0;
    uint32_t freq_out = 0;
    div = (CLK->AHB_CLK_CTRL & AHB_CLK_CTRL_AHB_DIV_Msk) >> AHB_CLK_CTRL_AHB_DIV_Pos;
    if(div == 0)
        div = 1;
    else
        div += 1;

    if(PMU->SYS_CLK_CTRL & CLK_SYS_SRCSEL_DPLL){
        freq_out = (PMU->DPLL_CTRL & DPLL_CTRL_CLK_DPLL_FREQ_SEL_AON_Msk);
        if(freq_out == CLK_DPLL_OUT_32M){
            SystemCoreClock = FREQ_32MHZ  / div;
        }
        else{
            SystemCoreClock = FREQ_48MHZ / div;
        }
    }
    else{
        SystemCoreClock = FREQ_32MHZ / div;
    }
}

static bool HwParamDataVerify(OTP_STRUCT_T *p_opt)
{
    uint8_t calc_checksum = 0;

    for (size_t i = 0x1E; i < 0x7B; i++)
    {
        calc_checksum += p_opt->d8[i];
    }

    if (calc_checksum != p_opt->m.ft_checksum)
    {
        return false;   // Calib data checksum failed
    }

    if (p_opt->m.ft_version >= 2)
    {
        calc_checksum = 0;
        for (size_t i = 0x80; i < 0xFF; i++)
        {
            calc_checksum += p_opt->d8[i];
        }

        if (calc_checksum != p_opt->m_v2.ft_checksum2)
        {
            return false;   // Calib data checksum2 failed
        }
    }

    return true;
}

__WEAK void ADC_SetCalirationParams(OTP_STRUCT_T *otp)
{
    return;
}

bool SystemHwParamLoader(OTP_STRUCT_T *otp)
{
    // Check if hw calibration data is valid
    if (!HwParamDataVerify(otp))
    {
        /* Mark chip as NoFT */
        isFtDataValid = false;
        /* Exit if Hw Parameter Data Checking Failed */
        memset(&otp->d8[0x00], 0x00, sizeof(OTP_STRUCT_T));  // clear the global otp structure
        return false;
    }

    // Note:
    // 1. Clock XTH and XTL may need Board-Level calibration on customers MP stage
    // 2. ADC calibration data is loaded when calling ADC APIs (e.g. ADC_Init(), ADC_OutputVoltage(), ..)
    PMU->ANA_MISC = (PMU->ANA_MISC & ~(0xFu << 4) & ~(0x7u << 17)) | (otp->m.pmu_vbg_trim << 4) | (otp->m.ipoly_trim << 17);
	PMU->ANA_HPLDO = (PMU->ANA_HPLDO & ~(0xFu << 4) & ~(0xfu << 20)) | (otp->m.hp_ldo_trim << 4) | (otp->m.flash_ldo_trim << 20);
    PMU->ANA_LPLDO = (PMU->ANA_LPLDO & ~(0xFu << 1) & ~(0xFu << 20)) | (otp->m.lpl_ldo_trim << 1) | (otp->m.lph_ldo_trim << 20);
    PMU->ANA_ANALDO = (PMU->ANA_ANALDO & ~(0xFu << 4)) | (otp->m.ana_ldo_trim << 4);
    PMU->BLD_CTRL = (PMU->BLD_CTRL & ~(0x7u << 6)) | (otp->m.bod_vref_trim << 6);
    PMU->RCH_CTRL = (PMU->RCH_CTRL & ~(0xFFu << 8)) | (otp->m.rch_trim << 8);
    PMU->RCL_CTRL = (PMU->RCL_CTRL & ~(0xFFFu << 4)) | (otp->m.rcl_coarse_trim << 4) | (otp->m.rcl_fine_trim << 8);
//    otp->m.buck_imax_cal_trim = (otp->m.buck_imax_cal_trim + 4 > 0x1F) ? 0x1F : (otp->m.buck_imax_cal_trim + 4);
//    PMU->ANA_BUCK = (PMU->ANA_BUCK & ~(0xFu << 4) & ~(0x1Fu << 15) & ~(0x1Fu << 20))
//            | (otp->m.buck_out_trim << 4) | (otp->m.buck_imax_cal_trim << 15) | (otp->m.buck_zero_cal_trim << 20);


    /*
     * Storing adc params related calibration. if adc symbol is not linked, 
     * this function would be handled as weak function.
     */
    ADC_SetCalirationParams(otp);

    return true;
}


#if USE_ASSERT

/**
 * @brief      Assert Error Message
 *
 * @param[in]  file  the source file name
 * @param[in]  line  line number
 *
 * @return     None
 *
 * @details    The function prints the source file name and line number where
 *             the ASSERT_PARAM() error occurs, and then stops in an infinite loop.
 */
void AssertError(uint8_t * file, uint32_t line)
{

    SYS_DBG(" wrong parameters.\r\n", file, line);

    /* Infinite loop */
    while(1) ;
}
#endif
