/**
 * @file xmc1_scu.c
 * @date 2016-01-12
 *
 * @cond
 *********************************************************************************************************************
 * XMClib v2.1.6 - XMC Peripheral Driver Library 
 *
 * Copyright (c) 2015-2016, Infineon Technologies AG
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,are permitted provided that the
 * following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
 * disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided with the distribution.
 *
 * Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote
 * products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE  FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY,OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * To improve the quality of the software, users are encouraged to share modifications, enhancements or bug fixes with
 * Infineon Technologies AG dave@infineon.com).
 *********************************************************************************************************************
 *
 * Change History
 * --------------
 *
 * 2015-02-20:
 *     - Initial <br>
 *
 * 2015-05-20:
 *     - XMC_SCU_StartTempMeasurement API is modified
 *     - XMC_ASSERT statements are added in XMC_SCU_INTERRUPT_SetEventHandler
 *
 * 2015-06-20:
 *     - XMC_SCU_INTERRUPT_EnableEvent,XMC_SCU_INTERRUPT_DisableEvent,
 *     - XMC_SCU_INTERRUPT_TriggerEvent,XMC_SCU_INTERUPT_GetEventStatus,
 *     - XMC_SCU_INTERRUPT_ClearEventStatus APIs are added
 *
 * 2015-09-23:
 *     - XMC1400 support added
 *
 * 2015-11-30:
 *     - Documentation improved <br>
 *
 * @endcond
 *
 */

/**
 *
 * @brief SCU low level driver API prototype definition for XMC1 family of microcontrollers <br>
 *
 * <b>Detailed description of file</b> <br>
 * APIs provided in this file cover the following functional blocks of SCU: <br>
 * -- GCU (APIs prefixed with XMC_SCU_GEN_) <br>
 * ----Temperature Monitoring, Voltage Monitoring, CCU Start etc
 *
 * -- CCU (APIs prefixed with XMC_SCU_CLOCK_)<br>
 * ---- Clock initialization, Clock Gating, Sleep Management etc
 *
 * -- RCU (APIs prefixed with XMC_SCU_RESET_) <br>
 * ---- Reset Init, Cause, Manual Reset Assert/Deassert etc
 *
 * -- INTERRUPT (APIs prefixed with XMC_SCU_INTERRUPT_)<br>
 * ---- Initialization, Manual Assert/Deassert, Acknowledge etc
 *
 */

/*********************************************************************************************************************
 * HEADER FILES
 ********************************************************************************************************************/
#include <xmc_scu.h>

#if UC_FAMILY == XMC1

/*********************************************************************************************************************
 * MACROS
 ********************************************************************************************************************/
#define SCU_IRQ_NUM             (3U)  /**< array index value for list of events that can generate SCU interrupt */
#define SCU_GCU_PASSWD_PROT_ENABLE  (195UL) /**< Password for enabling protection */
#define SCU_GCU_PASSWD_PROT_DISABLE (192UL) /**< Password for disabling protection */


#define XMC_SCU_CHECK_RTCCLKSRC(source)       ( (source == XMC_SCU_CLOCK_RTCCLKSRC_DCO2) || \
                                                (source == XMC_SCU_CLOCK_RTCCLKSRC_ERU_IOUT0) || \
                                                (source == XMC_SCU_CLOCK_RTCCLKSRC_ACMP0_OUT) || \
                                                (source == XMC_SCU_CLOCK_RTCCLKSRC_ACMP1_OUT) || \
                                                (source == XMC_SCU_CLOCK_RTCCLKSRC_ACMP2_OUT) ) /**< Used to verify
                                                                                                whether provided RTC
                                                                                                clock source is valid
                                                                                                or not */
#define SCU_GENERAL_INTCR_INTSEL_Msk SCU_GENERAL_INTCR0_INTSEL0_Msk /**< Mask value of Interrupt Source Select
                                                                         for Node 0 */
#define SCU_GENERAL_INTCR_INTSEL_Size SCU_GENERAL_INTCR0_INTSEL1_Pos /**< Bit position value of Interrupt Source Select
                                                                         for Node 1 */

#define ANA_TSE_T1   (0x10000F30U) /**< d is a constant data can be retrieved from Flash sector 0 to calculate OFFSET
                                         value for DCO calibration */
#define ANA_TSE_T2   (0x10000F31U) /**< e is a constant data can be retrieved from Flash sector 0 to calculate OFFSET
                                         value for DCO calibration */
#define DCO_ADJLO_T1 (0x10000F32U) /**< b is a constant data can be retrieved from Flash sector 0 to calculate OFFSET
                                         value for DCO calibration */
#define DCO_ADJLO_T2 (0x10000F33U) /**< a is a constant data can be retrieved from Flash sector 0 to calculate OFFSET
                                         value for DCO calibration */

#if UC_SERIES == XMC14
#define XMC_SCU_INTERRUPT_EVENT_MAX (64U) /**< Maximum supported SCU events for XMC14 device. */
#else
#define XMC_SCU_INTERRUPT_EVENT_MAX (32U)  /**< Maximum supported SCU events for XMC11/12/13 device. */
#endif

#if UC_SERIES == XMC14
#define DCO1_DIV2_FREQUENCY_KHZ_Q22_10 (48000U << 10) /**< used to configures main clock (MCLK) frequency to requested
                                                         frequency value during runtime for XMC14 device. */
#else
#define DCO1_DIV2_FREQUENCY_KHZ_Q24_8 (32000U << 8) /**< used to configures main clock (MCLK) frequency to requested
                                                         frequency value during runtime for XMC11/12/13  device. */
#endif

#define ROM_BmiInstallationReq \
        (*((uint32_t (**)(uint32_t requestedBmiValue))0x00000108U)) /**< Pointer to Request BMI installation routine is
                                                                         available inside ROM. */

#define ROM_CalcTemperature \
        (*((uint32_t (**)(void))0x0000010cU)) /**<  Pointer to Calculate chip temperature routine is
                                                    available inside ROM. */

#define ROM_CalcTSEVAR \
        (*((uint32_t (**)(uint32_t temperature))0x00000120U)) /**<  Pointer to Calculate target level for temperature
                                                                    comparison routine is available inside ROM. */
        
/*********************************************************************************************************************
 * LOCAL DATA
 ********************************************************************************************************************/
static XMC_SCU_INTERRUPT_EVENT_HANDLER_t event_handler_list[XMC_SCU_INTERRUPT_EVENT_MAX]; /**< For registering callback
                                                                                        functions on SCU event
                                                                                        occurrence. */

static XMC_SCU_INTERRUPT_EVENT_t event_masks[SCU_IRQ_NUM] =
{
  (XMC_SCU_INTERRUPT_EVENT_FLASH_ERROR |
   XMC_SCU_INTERRUPT_EVENT_FLASH_COMPLETED |
   XMC_SCU_INTERRUPT_EVENT_PESRAM |
   XMC_SCU_INTERRUPT_EVENT_PEUSIC0 |
#if defined(USIC1)
   XMC_SCU_INTERRUPT_EVENT_PEUSIC1 |
#endif
#if defined(CAN)
   XMC_SCU_INTERRUPT_EVENT_PEMCAN |
#endif
#if UC_SERIES == XMC14
   XMC_SCU_INTERRUPT_EVENT_LOSS_EXT_CLOCK |
#endif
   XMC_SCU_INTERRUPT_EVENT_LOCI),

  (XMC_SCU_INTERRUPT_EVENT_STDBYCLKFAIL |
#if UC_SERIES == XMC14
   XMC_SCU_INTERRUPT_EVENT_DCO1_OUT_SYNC |
#endif
   XMC_SCU_INTERRUPT_EVENT_VDDPI |
   XMC_SCU_INTERRUPT_EVENT_VDROP |
   XMC_SCU_INTERRUPT_EVENT_VCLIP |
   XMC_SCU_INTERRUPT_EVENT_TSE_DONE |
   XMC_SCU_INTERRUPT_EVENT_TSE_HIGH |
   XMC_SCU_INTERRUPT_EVENT_TSE_LOW |
   XMC_SCU_INTERRUPT_EVENT_WDT_WARN |
   XMC_SCU_INTERRUPT_EVENT_RTC_PERIODIC |
   XMC_SCU_INTERRUPT_EVENT_RTC_ALARM |
   XMC_SCU_INTERRUPT_EVENT_RTCCTR_UPDATED |
   XMC_SCU_INTERRUPT_EVENT_RTCATIM0_UPDATED |
   XMC_SCU_INTERRUPT_EVENT_RTCATIM1_UPDATED |
   XMC_SCU_INTERRUPT_EVENT_RTCTIM0_UPDATED |
   XMC_SCU_INTERRUPT_EVENT_RTCTIM1_UPDATED),

  (
#if UC_SERIES != XMC11
   XMC_SCU_INTERRUPT_EVENT_ORC0 |
   XMC_SCU_INTERRUPT_EVENT_ORC1 |
   XMC_SCU_INTERRUPT_EVENT_ORC2 |
   XMC_SCU_INTERRUPT_EVENT_ORC3 |
   XMC_SCU_INTERRUPT_EVENT_ORC4 |
   XMC_SCU_INTERRUPT_EVENT_ORC5 |
   XMC_SCU_INTERRUPT_EVENT_ORC6 |
   XMC_SCU_INTERRUPT_EVENT_ORC7 |
#endif
#if defined(COMPARATOR)
   XMC_SCU_INTERRUPT_EVENT_ACMP0 |
   XMC_SCU_INTERRUPT_EVENT_ACMP1 |
   XMC_SCU_INTERRUPT_EVENT_ACMP2 |
#if UC_SERIES == XMC14
   XMC_SCU_INTERRUPT_EVENT_ACMP3 |
#endif
#endif
   0)
}; /**<   Defines list of events that can generate SCU interrupt and also indicates SCU events mapping to corresponding
         service request number. These event mask values can be used to verify which event is triggered that corresponds
         to service request number during runtime. All the event items are tabulated as per service request sources list
         table of SCU. */

/*********************************************************************************************************************
 * LOCAL ROUTINES
 ********************************************************************************************************************/
/* Utility routine to perform frequency up scaling */
static void XMC_SCU_CLOCK_lFrequencyUpScaling(uint32_t curr_idiv, uint32_t idiv);

/* Utility routine to perform frequency down scaling */
static void XMC_SCU_CLOCK_lFrequencyDownScaling(uint32_t curr_idiv, uint32_t idiv);

/* Calculates the value which must be installed in ANATSEIx register to get indication in
   SCU_INTERRUPT->SRRAW  bit when the chip temperature is above/below some target/threshold. */
static uint32_t XMC_SCU_CalcTSEVAR(uint32_t temperature)
{
  uint32_t limit;

  XMC_ASSERT("XMC_SCU_CalcTSEVAR: temperature out of range", (temperature >= 233U) && (temperature <= 388U));

  limit = ROM_CalcTSEVAR(temperature);
  if (limit == 0U)
  {
    limit = ROM_CalcTSEVAR(temperature + 1U);
    if (limit == 0U)
    {
      limit = ROM_CalcTSEVAR(temperature - 1U);
    }
  }
  
  return (limit);
}

#if UC_SERIES == XMC14
/* This is a local function used to generate the delay until register get updated with new configured value.  */
static void delay(uint32_t cycles)
{
  while(cycles > 0U)
  {
    __NOP();
    cycles--;
  }
}
#endif

/*********************************************************************************************************************
 * API IMPLEMENTATION
 ********************************************************************************************************************/
 #ifdef XMC_ASSERT_ENABLE
/* API to verify SCU event weather it is valid event or not */
__STATIC_INLINE bool XMC_SCU_INTERRUPT_IsValidEvent(XMC_SCU_INTERRUPT_EVENT_t event)
{
  return ((event == XMC_SCU_INTERRUPT_EVENT_WDT_WARN) ||
          (event == XMC_SCU_INTERRUPT_EVENT_RTC_PERIODIC) ||
          (event == XMC_SCU_INTERRUPT_EVENT_RTC_ALARM) ||
          (event == XMC_SCU_INTERRUPT_EVENT_VDDPI) ||
#if defined(USIC1)
          (event == XMC_SCU_INTERRUPT_EVENT_PEUSIC1) ||
#endif
#if defined(CAN)
          (event == XMC_SCU_INTERRUPT_EVENT_PEMCAN) ||
#endif
#if UC_SERIES == XMC14
          (event == XMC_SCU_INTERRUPT_EVENT_LOSS_EXT_CLOCK) ||
          (event == XMC_SCU_INTERRUPT_EVENT_DCO1_OUT_SYNC) ||
#endif
#if defined(COMPARATOR)
          (event == XMC_SCU_INTERRUPT_EVENT_ACMP0) ||
          (event == XMC_SCU_INTERRUPT_EVENT_ACMP1) ||
          (event == XMC_SCU_INTERRUPT_EVENT_ACMP2) ||
#if UC_SERIES == XMC14
          (event == XMC_SCU_INTERRUPT_EVENT_ACMP3) ||
#endif
#endif
          (event == XMC_SCU_INTERRUPT_EVENT_VDROP) ||
#if UC_SERIES != XMC11
          (event == XMC_SCU_INTERRUPT_EVENT_ORC0) ||
          (event == XMC_SCU_INTERRUPT_EVENT_ORC1) ||
          (event == XMC_SCU_INTERRUPT_EVENT_ORC2) ||
          (event == XMC_SCU_INTERRUPT_EVENT_ORC3) ||
          (event == XMC_SCU_INTERRUPT_EVENT_ORC4) ||
          (event == XMC_SCU_INTERRUPT_EVENT_ORC5) ||
          (event == XMC_SCU_INTERRUPT_EVENT_ORC6) ||
          (event == XMC_SCU_INTERRUPT_EVENT_ORC7) ||
#endif
          (event == XMC_SCU_INTERRUPT_EVENT_LOCI) ||
          (event == XMC_SCU_INTERRUPT_EVENT_PESRAM) ||
          (event == XMC_SCU_INTERRUPT_EVENT_PEUSIC0) ||
          (event == XMC_SCU_INTERRUPT_EVENT_FLASH_ERROR) ||
          (event == XMC_SCU_INTERRUPT_EVENT_FLASH_COMPLETED) ||
          (event == XMC_SCU_INTERRUPT_EVENT_VCLIP) ||
          (event == XMC_SCU_INTERRUPT_EVENT_STDBYCLKFAIL) ||
          (event == XMC_SCU_INTERRUPT_EVENT_RTCCTR_UPDATED) ||
          (event == XMC_SCU_INTERRUPT_EVENT_RTCATIM0_UPDATED) ||
          (event == XMC_SCU_INTERRUPT_EVENT_RTCATIM1_UPDATED) ||
          (event == XMC_SCU_INTERRUPT_EVENT_RTCTIM0_UPDATED) ||
          (event == XMC_SCU_INTERRUPT_EVENT_RTCTIM1_UPDATED) ||
          (event == XMC_SCU_INTERRUPT_EVENT_TSE_DONE) ||
          (event == XMC_SCU_INTERRUPT_EVENT_TSE_HIGH) ||
          (event == XMC_SCU_INTERRUPT_EVENT_TSE_LOW));
}
 #endif

/* API to enable the SCU event */
void XMC_SCU_INTERRUPT_EnableEvent(const XMC_SCU_INTERRUPT_EVENT_t event)
{
  SCU_INTERRUPT->SRMSK |= (uint32_t)event;
#if UC_SERIES == XMC14
  SCU_INTERRUPT->SRMSK1 |= (uint32_t)(event >> 32U);
#endif
}

/* API to disable the SCU event */
void XMC_SCU_INTERRUPT_DisableEvent(const XMC_SCU_INTERRUPT_EVENT_t event)
{
  SCU_INTERRUPT->SRMSK &= ~(uint32_t)event;
#if UC_SERIES == XMC14
  SCU_INTERRUPT->SRMSK1 &= (uint32_t)~(event >> 32U);
#endif
}

/* API to trigger the SCU event */
void XMC_SCU_INTERRUPT_TriggerEvent(const XMC_SCU_INTERRUPT_EVENT_t event)
{
  SCU_INTERRUPT->SRSET |= (uint32_t)event;
#if UC_SERIES == XMC14
  SCU_INTERRUPT->SRSET1 |= (uint32_t)(event >> 32U);
#endif
}

/* API to get the SCU event status */
XMC_SCU_INTERRUPT_EVENT_t XMC_SCU_INTERUPT_GetEventStatus(void)
{
  XMC_SCU_INTERRUPT_EVENT_t tmp;

  tmp = SCU_INTERRUPT->SRRAW;
#if UC_SERIES == XMC14
  tmp |= ((int64_t)SCU_INTERRUPT->SRRAW1 << 32U);
#endif
  return (tmp);
}

/* API to clear the SCU event status */
void XMC_SCU_INTERRUPT_ClearEventStatus(const XMC_SCU_INTERRUPT_EVENT_t event)
{
  SCU_INTERRUPT->SRCLR |= (uint32_t)event;
#if UC_SERIES == XMC14
  SCU_INTERRUPT->SRCLR1 |= (uint32_t)(event >> 32U);
#endif
}

/* API to lock protected bitfields from being modified */
void XMC_SCU_LockProtectedBits(void)
{
  SCU_GENERAL->PASSWD = SCU_GCU_PASSWD_PROT_ENABLE;
}

/* API to make protected bitfields available for modification */
void XMC_SCU_UnlockProtectedBits(void)
{
  SCU_GENERAL->PASSWD = SCU_GCU_PASSWD_PROT_DISABLE;

  while(((SCU_GENERAL->PASSWD) & SCU_GENERAL_PASSWD_PROTS_Msk))
  {
    /* Loop until the lock is removed */
  }
}

/* API to initialize power supply monitoring unit */
void XMC_SCU_SupplyMonitorInit(const XMC_SCU_SUPPLYMONITOR_t *obj)
{
  uint32_t anavdel;
  uint32_t irqmask;

  anavdel = 0UL;

  anavdel |= (uint32_t)((obj-> ext_supply_threshold) << SCU_ANALOG_ANAVDEL_VDEL_SELECT_Pos);
  anavdel |= (uint32_t)((obj->ext_supply_monitor_speed) << SCU_ANALOG_ANAVDEL_VDEL_TIM_ADJ_Pos);

  if(true == (obj->enable_at_init))
  {
    anavdel |= (uint32_t)SCU_ANALOG_ANAVDEL_VDEL_EN_Msk;
  }
   SCU_ANALOG->ANAVDEL = (uint16_t) anavdel;

  irqmask = 0UL;

  if(true == (obj->enable_prewarning_int))
  {
    irqmask |= (uint32_t)SCU_INTERRUPT_SRMSK_VDDPI_Msk;
  }

  if(true == (obj->enable_vdrop_int))
  {
    irqmask |= (uint32_t)SCU_INTERRUPT_SRMSK_VDROPI_Msk;
  }

  if(true == (obj->enable_vclip_int))
  {
    irqmask |= (uint32_t)SCU_INTERRUPT_SRMSK_VCLIPI_Msk;
  }
  SCU_INTERRUPT->SRMSK |= (uint32_t)irqmask;
}

/* API to program lower temperature limit */
XMC_SCU_STATUS_t XMC_SCU_SetTempLowLimit(uint32_t limit)
{
  XMC_SCU_STATUS_t status = XMC_SCU_STATUS_OK;

  limit = XMC_SCU_CalcTSEVAR(limit);

  if (limit != 0) 
  {
    SCU_ANALOG->ANATSEIL = (uint16_t)limit;
  }
  else
  {
    status = XMC_SCU_STATUS_ERROR;
  }

  return (status);
}
  
/* API to program higher temperature limit */
XMC_SCU_STATUS_t XMC_SCU_SetTempHighLimit(uint32_t limit)
{
  XMC_SCU_STATUS_t status = XMC_SCU_STATUS_OK;

  limit = XMC_SCU_CalcTSEVAR(limit);

  if (limit != 0) 
  {
    SCU_ANALOG->ANATSEIH = (uint16_t)limit;
  }
  else
  {
    status = XMC_SCU_STATUS_ERROR;
  } 

  return (status);
}

/* API to program temperature limits as raw digital values into temperature sensor */
void XMC_SCU_SetRawTempLimits(const uint32_t lower_temp, const uint32_t upper_temp)
{
  SCU_ANALOG->ANATSEIH = (uint16_t)(upper_temp & SCU_ANALOG_ANATSEIH_TSE_IH_Msk);
  SCU_ANALOG->ANATSEIL = (uint16_t)(lower_temp & SCU_ANALOG_ANATSEIL_TSE_IL_Msk);
}

/* API to start temperature measurement */
void XMC_SCU_StartTempMeasurement(void)
{
  SCU_ANALOG->ANATSECTRL |= (uint16_t)SCU_ANALOG_ANATSECTRL_TSE_EN_Msk;
}

/* API to stop temperature measurement */
void XMC_SCU_StopTempMeasurement(void)
{
  SCU_ANALOG->ANATSECTRL &= (uint16_t)~SCU_ANALOG_ANATSECTRL_TSE_EN_Msk;
}

/* API to check if the temperature has gone past the ceiling */
bool XMC_SCU_HighTemperature(void)
{
  return ((SCU_INTERRUPT->SRRAW & SCU_INTERRUPT_SRRAW_TSE_HIGH_Msk) == SCU_INTERRUPT_SRRAW_TSE_HIGH_Msk);
}

/* API to check if the temperature is lower than normal */
bool XMC_SCU_LowTemperature(void)
{
  return ((SCU_INTERRUPT->SRRAW & SCU_INTERRUPT_SRRAW_TSE_LOW_Msk) == SCU_INTERRUPT_SRRAW_TSE_LOW_Msk);
}

/* API to retrieve the device temperature */
uint32_t XMC_SCU_GetTemperature(void)
{
  uint32_t temperature;

  temperature = (uint32_t)(SCU_ANALOG->ANATSEMON);

  return (temperature);
}

/* Calculates the die temperature value using ROM function */
uint32_t XMC_SCU_CalcTemperature(void)
{
  return (ROM_CalcTemperature());
}


/* API which initializes the clock tree ofthe device */
void XMC_SCU_CLOCK_Init(const XMC_SCU_CLOCK_CONFIG_t *const config)
{
  /* Remove protection */
  XMC_SCU_UnlockProtectedBits();

#if (UC_SERIES == XMC14)
  /* OSCHP source selection - OSC mode */

  if (config->oschp_mode != XMC_SCU_CLOCK_OSCHP_MODE_DISABLED)
  {
    if (OSCHP_GetFrequency() > 20000000U)
    {
      SCU_ANALOG->ANAOSCHPCTRL |= (uint16_t)SCU_ANALOG_ANAOSCHPCTRL_HYSCTRL_Msk;
    }

    SCU_ANALOG->ANAOSCHPCTRL = (uint16_t)(SCU_ANALOG->ANAOSCHPCTRL & ~(SCU_ANALOG_ANAOSCHPCTRL_SHBY_Msk | SCU_ANALOG_ANAOSCHPCTRL_MODE_Msk)) |
                                 config->oschp_mode;

    /* Enable OSC_HP oscillator watchdog*/
    SCU_CLK->OSCCSR |= SCU_CLK_OSCCSR_XOWDEN_Msk;

    do
    {
      /* Restart OSC_HP oscillator watchdog */
      SCU_INTERRUPT->SRCLR1 = SCU_INTERRUPT_SRCLR1_LOECI_Msk;
      SCU_CLK->OSCCSR |= SCU_CLK_OSCCSR_XOWDRES_Msk;

      /* Wait a few DCO2 cycles for the update of the clock detection result */
      delay(2500);

      /* check clock is ok */
    }
    while(SCU_INTERRUPT->SRRAW1 & SCU_INTERRUPT_SRRAW1_LOECI_Msk);
  }
  else
  {
    SCU_ANALOG->ANAOSCHPCTRL &= (uint16_t)~SCU_ANALOG_ANAOSCHPCTRL_MODE_Msk;
  }

  SCU_ANALOG->ANAOSCLPCTRL = (uint16_t)config->osclp_mode;

  SCU_CLK->CLKCR1 = (SCU_CLK->CLKCR1 & ~SCU_CLK_CLKCR1_DCLKSEL_Msk) |
                    config->dclk_src;

#endif
  /* Update PCLK selection mux. */
  SCU_CLK->CLKCR = (SCU_CLK->CLKCR & (uint32_t)~(SCU_CLK_CLKCR_PCLKSEL_Msk | SCU_CLK_CLKCR_RTCCLKSEL_Msk)) |
                   config->rtc_src |
                   config->pclk_src;

  /* Close the lock opened above. */
  XMC_SCU_LockProtectedBits();

  /* Update the dividers now */
  XMC_SCU_CLOCK_ScaleMCLKFrequency(config->idiv, config->fdiv);

}

/* API which selects one of the available parent clock nodes for a given child clock node */
void XMC_SCU_CLOCK_SetRtcClockSource(const XMC_SCU_CLOCK_RTCCLKSRC_t source)
{
  XMC_ASSERT("XMC_SCU_CLOCK_SetRtcSourceClock:Wrong Parent Clock", XMC_SCU_CHECK_RTCCLKSRC(source));

  XMC_SCU_UnlockProtectedBits();

  SCU_CLK->CLKCR = (SCU_CLK->CLKCR & (uint32_t)~SCU_CLK_CLKCR_RTCCLKSEL_Msk) |
                   source;

  XMC_SCU_LockProtectedBits();
}

/* API to program the divider placed between fperiph and its parent */
void XMC_SCU_CLOCK_SetFastPeripheralClockSource(const XMC_SCU_CLOCK_PCLKSRC_t source)
{
  XMC_SCU_UnlockProtectedBits();

  SCU_CLK->CLKCR = (SCU_CLK->CLKCR & (uint32_t)~SCU_CLK_CLKCR_PCLKSEL_Msk) |
                   source;

  XMC_SCU_LockProtectedBits();
}

/* API which gates a clock node at its source */
void XMC_SCU_CLOCK_GatePeripheralClock(const XMC_SCU_PERIPHERAL_CLOCK_t peripheral)
{
  XMC_SCU_UnlockProtectedBits();
  SCU_CLK->CGATSET0 |= (uint32_t)peripheral;
  XMC_SCU_LockProtectedBits();
}

/* API which ungates a clock note at its source */
void XMC_SCU_CLOCK_UngatePeripheralClock(const XMC_SCU_PERIPHERAL_CLOCK_t peripheral)
{
  XMC_SCU_UnlockProtectedBits();
  SCU_CLK->CGATCLR0 |= (uint32_t)peripheral;
  while ((SCU_CLK->CLKCR) & SCU_CLK_CLKCR_VDDC2LOW_Msk)
  {
    /* Wait voltage suply stabilization */
  }
  XMC_SCU_LockProtectedBits();
}

/* Checks the status of peripheral clock gating */
bool XMC_SCU_CLOCK_IsPeripheralClockGated(const XMC_SCU_PERIPHERAL_CLOCK_t peripheral)
{
  return (bool)((SCU_CLK->CGATSTAT0 & peripheral) != 0);
}

/* This API configures main clock (MCLK) frequency to requested frequency value. */
void XMC_SCU_CLOCK_SetMCLKFrequency(uint32_t freq_khz)
{
  uint32_t ratio;
  uint32_t ratio_int;
  uint32_t ratio_frac;

#if UC_SERIES == XMC14
  if (((SCU_CLK->CLKCR1) & SCU_CLK_CLKCR1_DCLKSEL_Msk) == 0U)
  {
     ratio = DCO1_DIV2_FREQUENCY_KHZ_Q22_10 / freq_khz;
  }
  else
  {
     ratio = ((OSCHP_GetFrequency() / 1000U) << 10U) / freq_khz;
  }

  /* Manage overflow */
  if (ratio > 0xffffffU)
  {
    ratio = 0xffffffU;
  }

  ratio_int = ratio >> 10U;
  ratio_frac = ratio & 0x3ffU;
#else
    ratio = DCO1_DIV2_FREQUENCY_KHZ_Q24_8 / freq_khz;

  /* Manage overflow */
  if (ratio > 0xffffU)
  {
    ratio = 0xffffU;
  }

  ratio_int = ratio >> 8U;
  ratio_frac = ratio & 0xffU;
#endif

  XMC_SCU_CLOCK_ScaleMCLKFrequency(ratio_int, ratio_frac);
}


/* A utility routine which updates the fractional dividers in steps */
void XMC_SCU_CLOCK_ScaleMCLKFrequency(uint32_t idiv, uint32_t fdiv)
{
  /* Find out current and target value of idiv */
  uint32_t curr_idiv;

  XMC_SCU_UnlockProtectedBits();

  /* Take a snapshot of value already programmed into IDIV */
  curr_idiv = (SCU_CLK->CLKCR & SCU_CLK_CLKCR_IDIV_Msk) >> SCU_CLK_CLKCR_IDIV_Pos;

#if (UC_SERIES == XMC14)
  SCU_CLK->CLKCR1 = (SCU_CLK->CLKCR1 & (uint32_t)~(SCU_CLK_CLKCR1_FDIV_Msk)) |
                   (uint32_t)((fdiv >> 8U) << SCU_CLK_CLKCR1_FDIV_Pos);

  SCU_CLK->CLKCR = (SCU_CLK->CLKCR & (uint32_t)~(SCU_CLK_CLKCR_FDIV_Msk | SCU_CLK_CLKCR_CNTADJ_Pos)) |
                   (uint32_t)((fdiv & 0xffU) << SCU_CLK_CLKCR_FDIV_Pos) |
                   (uint32_t)(1023UL << SCU_CLK_CLKCR_CNTADJ_Pos);
#else
  SCU_CLK->CLKCR = (SCU_CLK->CLKCR & (uint32_t)~(SCU_CLK_CLKCR_FDIV_Msk | SCU_CLK_CLKCR_CNTADJ_Pos)) |
                   (uint32_t)(fdiv << SCU_CLK_CLKCR_FDIV_Pos) |
                   (uint32_t)(1023UL << SCU_CLK_CLKCR_CNTADJ_Pos);
#endif

  while ((SCU_CLK->CLKCR)& SCU_CLK_CLKCR_VDDC2LOW_Msk)
  {
    /* Spin until the core supply stabilizes */
  }

  if(curr_idiv <= idiv)
  {
    /* Requested IDIV is greater than currently programmed IDIV. So downscale the frequency */
    XMC_SCU_CLOCK_lFrequencyDownScaling(curr_idiv, idiv);
  }
  else
  {
    /* Requested IDIV is lower than currently programmed IDIV. So upscale the frequency */
    XMC_SCU_CLOCK_lFrequencyUpScaling(curr_idiv, idiv);
  }

  SCU_CLK->CLKCR = (SCU_CLK->CLKCR & (uint32_t)~(SCU_CLK_CLKCR_IDIV_Msk | SCU_CLK_CLKCR_CNTADJ_Msk)) |
                   (uint32_t)(idiv << SCU_CLK_CLKCR_IDIV_Pos) | (uint32_t)(1023UL << SCU_CLK_CLKCR_CNTADJ_Pos);

  while ((SCU_CLK->CLKCR) & SCU_CLK_CLKCR_VDDC2LOW_Msk)
  {
    /* Wait voltage suply stabilization */
  }

  XMC_SCU_LockProtectedBits();

  SystemCoreClockUpdate();

}

/* Utility routine to perform frequency up scaling */
static void XMC_SCU_CLOCK_lFrequencyUpScaling(uint32_t curr_idiv, uint32_t target_idiv)
{
  while (curr_idiv > (target_idiv * 4UL))
  {
    curr_idiv = (uint32_t)(curr_idiv >> 2UL);   /* Divide by 4. */

    SCU_CLK->CLKCR = (SCU_CLK->CLKCR & (uint32_t)~(SCU_CLK_CLKCR_IDIV_Msk | SCU_CLK_CLKCR_CNTADJ_Msk)) |
                     (uint32_t)(curr_idiv << SCU_CLK_CLKCR_IDIV_Pos) | (uint32_t)(1023UL << SCU_CLK_CLKCR_CNTADJ_Pos);

    while (SCU_CLK->CLKCR & SCU_CLK_CLKCR_VDDC2LOW_Msk)
    {
    /* Wait voltage suply stabilization */
    }
  }
}

/* Utility routine to perform frequency down scaling */
static void XMC_SCU_CLOCK_lFrequencyDownScaling(uint32_t curr_idiv, uint32_t target_idiv)
{

  while ((curr_idiv * 4UL) < target_idiv)
  {
    if(0U == curr_idiv)
    {
      curr_idiv = 1U;
    }
    curr_idiv  = (uint32_t)(curr_idiv << 2UL);   /* Multiply by 4. */
    SCU_CLK->CLKCR = (SCU_CLK->CLKCR & (uint32_t)~(SCU_CLK_CLKCR_IDIV_Msk|SCU_CLK_CLKCR_CNTADJ_Pos)) |
                     (uint32_t)(curr_idiv << SCU_CLK_CLKCR_IDIV_Pos) |
                     (uint32_t)(1023UL << SCU_CLK_CLKCR_CNTADJ_Pos);

    while (SCU_CLK->CLKCR & SCU_CLK_CLKCR_VDDC2LOW_Msk)
    {
    /* Wait voltage suply stabilization */
    }
  }
}

/*
 * API to retrieve clock frequency of peripherals on the peripheral bus using a shared functional clock
 */
uint32_t XMC_SCU_CLOCK_GetPeripheralClockFrequency(void)
{
  return (SystemCoreClock);
}

/* Provides the clock frequency of peripherals on the peripheral bus that are using a shared functional clock */
uint32_t XMC_SCU_CLOCK_GetFastPeripheralClockFrequency(void)
{
  return (SystemCoreClock << ((SCU_CLK->CLKCR & SCU_CLK_CLKCR_PCLKSEL_Msk) >> SCU_CLK_CLKCR_PCLKSEL_Pos));
}

/* DCO1 clock frequency can be calibrated during runtime to achieve a better accuracy */
void XMC_SCU_CLOCK_CalibrateOscillatorOnTemperature(int32_t temperature)
{
  int32_t a;
  int32_t b;
  int32_t d;
  int32_t e;
  int32_t offset;

  a = *((uint8_t*)DCO_ADJLO_T2);
  b = *((uint8_t*)DCO_ADJLO_T1);
  d = *((uint8_t*)ANA_TSE_T1);
  e = *((uint8_t*)ANA_TSE_T2);

  offset = b + (((a - b) * (temperature - d)) / (e - d));
  offset &= SCU_ANALOG_ANAOFFSET_ADJL_OFFSET_Msk;

  XMC_SCU_UnlockProtectedBits();
  SCU_ANALOG->ANAOFFSET = (uint16_t)offset;
  XMC_SCU_LockProtectedBits();
}

/*
 * API to assign the event handler function to be executed on occurrence of the selected event
 */
XMC_SCU_STATUS_t XMC_SCU_INTERRUPT_SetEventHandler(XMC_SCU_INTERRUPT_EVENT_t event, XMC_SCU_INTERRUPT_EVENT_HANDLER_t handler)
{
  uint32_t index;
  XMC_SCU_STATUS_t status;

  XMC_ASSERT("XMC_SCU_INTERRUPT_SetEventHandler: Invalid event", XMC_SCU_INTERRUPT_IsValidEvent(event));
  XMC_ASSERT("XMC_SCU_INTERRUPT_SetEventHandler: Invalid handler", handler != NULL);

  index = 0U;

  while (((event & ((XMC_SCU_INTERRUPT_EVENT_t)1 << index)) == 0U) && (index < XMC_SCU_INTERRUPT_EVENT_MAX))
  {
    index++;
  }

  if (index == XMC_SCU_INTERRUPT_EVENT_MAX)
  {
    status = XMC_SCU_STATUS_ERROR;
  }
  else
  {
    event_handler_list[index] = handler;
    status = XMC_SCU_STATUS_OK;
  }

  return (status);
}

/*
 * A common function to execute callback functions for multiple events
 */
void XMC_SCU_IRQHandler(uint32_t sr_num)
{
  XMC_ASSERT("XMC_SCU_IRQHandler: Invalid sr_num", sr_num < SCU_IRQ_NUM);

  uint32_t index;
  XMC_SCU_INTERRUPT_EVENT_t event;
  XMC_SCU_INTERRUPT_EVENT_HANDLER_t event_handler;

  index = 0U;
  event = XMC_SCU_INTERUPT_GetEventStatus() & event_masks[sr_num];
  XMC_SCU_INTERRUPT_ClearEventStatus(event);

  while ((event != 0) && (index < XMC_SCU_INTERRUPT_EVENT_MAX))
  {
    if ((event & ((XMC_SCU_INTERRUPT_EVENT_t)1 << index)) != 0U)
    {
      event &= ~((XMC_SCU_INTERRUPT_EVENT_t)1 << index);
      event_handler = event_handler_list[index];
      if (event_handler != NULL)
      {
        event_handler();
      }

      /* break; XMC1: Only PULSE interrupts */
    }
    index++;
  }
}

#if (UC_SERIES == XMC14)
/* DCO1 clock frequency can be calibrated during runtime to achieve a better accuracy */
void XMC_SCU_CLOCK_EnableDCO1ExtRefCalibration(XMC_SCU_CLOCK_SYNC_CLKSRC_t sync_clk, uint32_t prescaler, uint32_t syn_preload)
{

  XMC_SCU_UnlockProtectedBits();
  SCU_ANALOG->ANASYNC2 = (uint16_t)(prescaler << SCU_ANALOG_ANASYNC2_PRESCALER_Pos);
  SCU_ANALOG->ANASYNC1 = (uint16_t)(syn_preload |
                         sync_clk |
                         SCU_ANALOG_ANASYNC1_SYNC_DCO_EN_Msk);

  XMC_SCU_LockProtectedBits();

}

/* This function stops the automatic DCO1 calibration based on the selected clock source */
void XMC_SCU_CLOCK_DisableDCO1ExtRefCalibration(void)
{
  XMC_SCU_UnlockProtectedBits();
  SCU_ANALOG->ANASYNC2 = 0U;
  SCU_ANALOG->ANASYNC1 = 0U;
  XMC_SCU_LockProtectedBits();
}

/* This functions checks the status of the synchronisation */
bool XMC_SCU_CLOCK_IsDCO1ExtRefCalibrationReady(void)
{
  return (bool)((SCU_ANALOG->ANASYNC2 & SCU_ANALOG_ANASYNC2_SYNC_READY_Msk) != 0U);
}

/**
 * This function enables the watchdog on the DCO1 frequency
 */
void XMC_SCU_CLOCK_EnableDCO1OscillatorWatchdog(void)
{
  SCU_CLK->OSCCSR |= SCU_CLK_OSCCSR_OWDEN_Msk;
}

/**
 * This function disables the watchdog on the DCO1 frequency
 */
void XMC_SCU_CLOCK_DisableDCO1OscillatorWatchdog(void)
{
  SCU_CLK->OSCCSR &= ~SCU_CLK_OSCCSR_OWDEN_Msk;
}

/**
 * This function clears the status of the watchdog on the DCO1 frequency
 */
void XMC_SCU_CLOCK_ClearDCO1OscillatorWatchdogStatus(void)
{
  SCU_CLK->OSCCSR |= SCU_CLK_OSCCSR_OWDRES_Msk;
}

/**
 * This function checks if the DCO1 frequency is in the limits of the watchdog.
 */
bool XMC_SCU_CLOCK_IsDCO1ClockFrequencyUsable(void)
{
  return ((SCU_CLK->OSCCSR & (SCU_CLK_OSCCSR_OSC2L_Msk | SCU_CLK_OSCCSR_OSC2H_Msk)) == 0U);
}

/* This function selects service request source for a NVIC interrupt node */
void XMC_SCU_SetInterruptControl(uint8_t irq_number, XMC_SCU_IRQCTRL_t source)
{
  XMC_ASSERT("XMC_SCU_SetInterruptControl: Invalid irq_number", irq_number < 32);
  XMC_ASSERT("XMC_SCU_SetInterruptControl: Invalid source", (source >> 8) == irq_number);

  source &= 0x3U;
  if (irq_number < 16U)
  {
    SCU_GENERAL->INTCR0 = (SCU_GENERAL->INTCR0 & ~(SCU_GENERAL_INTCR_INTSEL_Msk << (irq_number * SCU_GENERAL_INTCR_INTSEL_Size))) |
                          (source << (irq_number * SCU_GENERAL_INTCR_INTSEL_Size));
  }
  else
  {
    irq_number &= 0x0fU;
    SCU_GENERAL->INTCR1 = (SCU_GENERAL->INTCR1 & ~(SCU_GENERAL_INTCR_INTSEL_Msk << (irq_number  * SCU_GENERAL_INTCR_INTSEL_Size))) |
                          (source << (irq_number  * SCU_GENERAL_INTCR_INTSEL_Size));
  }
}

#endif
#endif /* UC_FAMILY == XMC1 */
