/**************************************************************************** * arch/arm/src/s32k1xx/s32k1xx_clockconfig.c * * Copyright (C) 2019 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name NuttX 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 OWNER 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. * * Much of the logic within this file derives heavily from NXP sample code * for the S32K1xx MCUs. That sample code has this licensing information: * * Copyright (c) 2013 - 2015, Freescale Semiconductor, Inc. * Copyright 2016-2018 NXP * All rights reserved. * * THIS SOFTWARE IS PROVIDED BY NXP "AS IS" AND ANY EXPRESSED 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 NXP OR ITS 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. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include "arm_internal.h" #include "hardware/s32k1xx_scg.h" #include "hardware/s32k1xx_smc.h" #include "hardware/s32k1xx_sim.h" #include "hardware/s32k1xx_pmc.h" #include "s32k1xx_periphclocks.h" #include "s32k1xx_clockconfig.h" #include "s32k1xx_start.h" #include /* Include last. May have dependencies */ /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* Temporary system clock source configurations. */ #define TMP_SIRC_CLK 0 #define TMP_FIRC_CLK 1 #define TMP_SOSC_CLK 2 #define TMP_SPLL_CLK 3 #define TMP_SYS_DIV 0 #define TMP_BUS_DIV 1 #define TMP_SLOW_DIV 2 #define TMP_SYS_CLK_NO 4 #define TMP_SYS_DIV_NO 3 /* Supports arrays of maximum clock frequencies of system clocks in all * power modes */ #define MODES_MAX_NO 7 #define SYS_CLK_MAX_NO 3 #define CORE_CLK_INDEX 0 #define BUS_CLK_INDEX 1 #define SLOW_CLK_INDEX 2 /* Time to wait for clocks to stabilize, ie., number of cycles when core * runs at maximum speed - 112 MHz. */ #define SIRC_STABILIZATION_TIMEOUT 100 #define FIRC_STABILIZATION_TIMEOUT 20 #define SOSC_STABILIZATION_TIMEOUT 3205000 #define SPLL_STABILIZATION_TIMEOUT 1000 /* System PLL reference clock after SCG_SPLLCFG[PREDIV] should be in the * range of SCG_SPLL_REF_MIN to SCG_SPLL_REF_MAX. */ #define SCG_SPLL_REF_MIN 8000000 #define SCG_SPLL_REF_MAX 32000000 /* Power management definitions */ #ifndef OK #define OK 0 #endif /**************************************************************************** * Private Function Declarations ****************************************************************************/ #ifdef CONFIG_PM static void up_pm_notify(struct pm_callback_s *cb, int dowmin, enum pm_state_e pmstate); static int up_pm_prepare(struct pm_callback_s *cb, int domain, enum pm_state_e pmstate); #endif /**************************************************************************** * Private Types ****************************************************************************/ /**************************************************************************** * Private Data ****************************************************************************/ static const uint8_t g_tmp_sysclk[TMP_SYS_CLK_NO][TMP_SYS_DIV_NO] = { { 1, /* SIRC SYS_CLK divider, range 1..16 */ 1, /* SIRC BUS_CLK divider, range 1..16 */ 2 /* SIRC SLOW_CLK divider, range 1..16 */ }, { 1, /* FIRC SYS_CLK divider, range 1..16 */ 2, /* FIRC BUS_CLK divider, range 1..16 */ 4 /* FIRC SLOW_CLK divider, range 1..16 */ }, { 1, /* SOSC SYS_CLK divider, range 1..16 */ 2, /* SOSC BUS_CLK divider, range 1..16 */ 2 /* SOSC SLOW_CLK divider, range 1..16 */ }, { 3, /* SPLL SYS_CLK divider, range 1..16 */ 2, /* SPLL BUS_CLK divider, range 1..16 */ 2 /* SPLL SLOW_CLK divider, range 1..16 */ } }; /* The maximum clock frequencies of system clocks in all power modes */ /* SYS_CLK BUS_CLK SLOW_CLK */ static const uint32_t g_vlpr_maxsysclks[MODES_MAX_NO][SYS_CLK_MAX_NO] = { { 0ul, 0ul, 0ul }, /* Invalid entry */ { 4000000ul, 4000000ul, 1000000ul }, /* Maximum frequencies when system clock is SOSC */ { 4000000ul, 4000000ul, 1000000ul }, /* Maximum frequencies when system clock is SIRC */ { 4000000ul, 4000000ul, 1000000ul }, /* Maximum frequencies when system clock is FIRC */ { 0ul, 0ul, 0ul }, /* Invalid entry */ { 0ul, 0ul, 0ul }, /* Invalid entry */ { 4000000ul, 4000000ul, 1000000ul }, /* Maximum frequencies when system clock is SPLL */ }; /* SYS_CLK BUS_CLK SLOW_CLK */ static const uint32_t g_run_maxsysclks[MODES_MAX_NO][SYS_CLK_MAX_NO] = { { 0ul, 0ul, 0ul }, /* Invalid entry */ { 80000000ul, 48000000ul, 26670000ul }, /* Maximum frequencies when system clock is SOSC */ { 80000000ul, 48000000ul, 26670000ul }, /* Maximum frequencies when system clock is SIRC */ { 80000000ul, 48000000ul, 26670000ul }, /* Maximum frequencies when system clock is FIRC */ { 0ul, 0ul, 0ul }, /* Invalid entry */ { 0ul, 0ul, 0ul }, /* Invalid entry */ { 80000000ul, 40000000ul, 26670000ul }, /* Maximum frequencies when system clock is SPLL */ }; #ifdef CONFIG_S32K1XX_HAVE_HSRUN /* SYS_CLK BUS_CLK SLOW_CLK */ static const uint32_t g_hsrun_maxsysclks[MODES_MAX_NO][SYS_CLK_MAX_NO] = { { 0ul, 0ul, 0ul }, /* Invalid entry */ { 112000000ul, 56000000ul, 28000000ul }, /* Maximum frequencies when system clock is SOSC */ { 112000000ul, 56000000ul, 28000000ul }, /* Maximum frequencies when system clock is SIRC */ { 112000000ul, 56000000ul, 28000000ul }, /* Maximum frequencies when system clock is FIRC */ { 0ul, 0ul, 0ul }, /* Invalid entry */ { 0ul, 0ul, 0ul }, /* Invalid entry */ { 112000000ul, 56000000ul, 28000000ul }, /* Maximum frequencies when system clock is SPLL */ }; #endif #if 0 /* Not currently used */ static uint32_t g_rtc_clkin; /* RTC CLKIN clock */ #endif #if 0 /* Not currently used */ static uint32_t g_tclkfreq[NUMBER_OF_TCLK_INPUTS]; /* TCLKx clocks */ #endif #ifdef CONFIG_PM static struct pm_callback_s g_clock_pmcb = { .notify = up_pm_notify, .prepare = up_pm_prepare, }; #endif /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: s32k1xx_get_scgclk_source * * Description: * Gets SCG current system clock source * * Input Parameters: * None * * Returned Value: * The current system clock source. * ****************************************************************************/ static inline uint32_t s32k1xx_get_scgclk_source(void) { return ((getreg32(S32K1XX_SCG_CSR) & SCG_CSR_SCS_MASK) >> SCG_CSR_SCS_SHIFT); } /**************************************************************************** * Name: s32k1xx_get_soscfreq * * Description: * Gets SCG System OSC clock frequency (SOSC). * * Input Parameters: * None * * Returned Value: * The SOSC frequency. Zero is returned if the SOSC is invalid. * ****************************************************************************/ static uint32_t s32k1xx_get_soscfreq(void) { /* Check if the SOSC is valid */ if ((getreg32(S32K1XX_SCG_SOSCCSR) & SCG_SOSCCSR_SOSCVLD) != 0) { return BOARD_XTAL_FREQUENCY; } else { return 0; } } /**************************************************************************** * Name: s32k1xx_get_sircfreq * * Description: * Gets SCG Slow IRC clock frequency (SIRC). * * Input Parameters: * None * * Returned Value: * The SIRC frequency. Zero is returned if the SIRC is invalid. * ****************************************************************************/ static uint32_t s32k1xx_get_sircfreq(void) { /* Check if the SIRC is valid */ if ((getreg32(S32K1XX_SCG_SIRCCSR) & SCG_SIRCCSR_SIRCVLD) != 0) { /* Only high range is supported */ if ((getreg32(S32K1XX_SCG_SIRCCFG) & SCG_SIRCCFG_RANGE) != 0) { return SCG_SIRC_HIGHRANGE_FREQUENCY; } } return 0; } /**************************************************************************** * Name: s32k1xx_get_fircfreq * * Description: * Gets SCG Fast IRC clock frequency (FIRC). * * Input Parameters: * None * * Returned Value: * The FIRC frequency. Zero is returned if the FIRC is invalid. * ****************************************************************************/ static uint32_t s32k1xx_get_fircfreq(void) { /* Check if the FIRC is valid */ if ((getreg32(S32K1XX_SCG_FIRCCSR) & SCG_FIRCCSR_FIRCVLD) != 0) { return SCG_FIRC_FREQUENCY0; } else { return 0; } } /**************************************************************************** * Name: s32k1xx_get_spllfreq * * Description: * Gets SCG System PLL clock frequency (SPLL). * * Input Parameters: * None * * Returned Value: * The SPLL frequency. Zero is returned if the SPLL is invalid. * ****************************************************************************/ #ifdef CONFIG_S32K1XX_HAVE_SPLL static uint32_t s32k1xx_get_spllfreq(void) { uint32_t freq; uint32_t regval; uint32_t prediv; uint32_t mult; /* Check if the SPLL is valid */ if ((getreg32(S32K1XX_SCG_SPLLCSR) & SCG_SPLLCSR_SPLLVLD) != 0) { /* Get System Oscillator frequency. */ freq = s32k1xx_get_soscfreq(); if (freq != 0) { regval = getreg32(S32K1XX_SCG_SPLLCFG); prediv = ((regval & SCG_SPLLCFG_PREDIV_MASK) >> SCG_SPLLCFG_PREDIV_SHIFT) + 1; mult = ((regval & SCG_SPLLCFG_MULT_MASK) >> SCG_SPLLCFG_MULT_SHIFT) + 16; freq /= prediv; freq *= mult; freq >>= 1; /* Divide VCO by 2. */ } return freq; } else { return 0; } } #endif /**************************************************************************** * Name: s32k1xx_get_srcfreq * * Description: * Return the clock source frequency. * * Input Parameters: * src - Identities the clock source * * Returned Values: * The requested clock source frequency. Zero is returned on any error. * ****************************************************************************/ static uint32_t s32k1xx_get_srcfreq(enum scg_system_clock_src_e src) { uint32_t srcfreq = 0; switch (src) { case SCG_SYSTEM_CLOCK_SRC_SYS_OSC: srcfreq = s32k1xx_get_soscfreq(); break; case SCG_SYSTEM_CLOCK_SRC_SIRC: srcfreq = s32k1xx_get_sircfreq(); break; case SCG_SYSTEM_CLOCK_SRC_FIRC: srcfreq = s32k1xx_get_fircfreq(); break; #ifdef CONFIG_S32K1XX_HAVE_SPLL case SCG_SYSTEM_CLOCK_SRC_SYS_PLL: srcfreq = s32k1xx_get_spllfreq(); break; #endif default: break; } return srcfreq; } /**************************************************************************** * Name: s32k1xx_set_sysclk_cfg * * Description: * This function sets the system configuration for the specified mode. * * Input Parameters: * mode - * config - * * Returned Value: * Zero (OK) is returned a success; A negated errno value is returned on * any failure. * ****************************************************************************/ static int s32k1xx_set_sysclk_cfg(enum scg_system_clock_mode_e mode, const struct scg_system_clock_config_s *config) { uint32_t srcfreq = 0; uint32_t sysfreq_mul = (uint32_t)config->divcore; uint32_t busfreq_mul = (uint32_t)config->divcore * (uint32_t)config->divbus; uint32_t slowfreq_mul = (uint32_t)config->divcore * (uint32_t)config->divslow; uint32_t regval; int ret = OK; DEBUGASSERT(mode != SCG_SYSTEM_CLOCK_MODE_CURRENT); srcfreq = s32k1xx_get_srcfreq(config->src) >> 4; switch (mode) { case SCG_SYSTEM_CLOCK_MODE_RUN: /* Run mode */ /* Verify the frequencies of sys, bus and slow clocks. */ if ((srcfreq > (sysfreq_mul * (g_run_maxsysclks[(uint32_t)config->src][CORE_CLK_INDEX] >> 4))) || (srcfreq > (busfreq_mul * (g_run_maxsysclks[(uint32_t)config->src][BUS_CLK_INDEX] >> 4))) || (srcfreq > (slowfreq_mul * (g_run_maxsysclks[(uint32_t)config->src][SLOW_CLK_INDEX] >> 4)))) { /* Configuration for the next system clock source is not * valid. */ ret = -EINVAL; } else { regval = (((uint32_t)config->src << SCG_RCCR_SCS_SHIFT) | SCG_RCCR_DIVCORE(config->divcore) | SCG_RCCR_DIVBUS(config->divbus) | SCG_RCCR_DIVSLOW(config->divslow)); putreg32(regval, S32K1XX_SCG_RCCR); } break; case SCG_SYSTEM_CLOCK_MODE_VLPR: /* Very Low Power Run mode */ DEBUGASSERT(SCG_SYSTEM_CLOCK_SRC_SIRC == config->src); /* Verify the frequencies of sys, bus and slow clocks. */ if ((srcfreq > (sysfreq_mul * (g_vlpr_maxsysclks[(uint32_t)config->src][CORE_CLK_INDEX] >> 4))) || (srcfreq > (busfreq_mul * (g_vlpr_maxsysclks[(uint32_t)config->src][BUS_CLK_INDEX] >> 4))) || (srcfreq > (slowfreq_mul * (g_vlpr_maxsysclks[(uint32_t)config->src][SLOW_CLK_INDEX] >> 4)))) { /* Configuration for the next system clock source is not * valid. */ ret = -EINVAL; } else { regval = (((uint32_t)config->src << SCG_VCCR_SCS_SHIFT) | SCG_VCCR_DIVCORE(config->divcore) | SCG_VCCR_DIVBUS(config->divbus) | SCG_VCCR_DIVSLOW(config->divslow)); putreg32(regval, S32K1XX_SCG_VCCR); } break; #ifdef CONFIG_S32K1XX_HAVE_HSRUN case SCG_SYSTEM_CLOCK_MODE_HSRUN: /* High Speed Run mode. */ DEBUGASSERT(SCG_SYSTEM_CLOCK_SRC_FIRC == config->src || SCG_SYSTEM_CLOCK_SRC_SYS_PLL == config->src); /* Verify the frequencies of sys, bus and slow clocks. */ if ((srcfreq > (sysfreq_mul * (g_hsrun_maxsysclks[(uint32_t)config->src][CORE_CLK_INDEX] >> 4))) || (srcfreq > (busfreq_mul * (g_hsrun_maxsysclks[(uint32_t)config->src][BUS_CLK_INDEX] >> 4))) || (srcfreq > (slowfreq_mul * (g_hsrun_maxsysclks[(uint32_t)config->src][SLOW_CLK_INDEX] >> 4)))) { /* Configuration for the next system clock source is not * valid. */ ret = -EINVAL; } else { regval = (((uint32_t)config->src << SCG_HCCR_SCS_SHIFT) | SCG_HCCR_DIVCORE(config->divcore) | SCG_HCCR_DIVBUS(config->divbus) | SCG_HCCR_DIVSLOW(config->divslow)); putreg32(regval, S32K1XX_SCG_HCCR); } break; #endif default: /* Invalid mode */ DEBUGPANIC(); break; } return ret; } /**************************************************************************** * Name: s32k1xx_transition_systemclock * * Description: * Transition to a new system clock. * * Input Parameters: * cfg - Describes the new system clock configuration * * Returned Value: * Zero (OK) is returned a success; A negated errno value is returned on * any failure. * ****************************************************************************/ static int s32k1xx_transition_systemclock(const struct scg_system_clock_config_s *cfg) { enum scg_system_clock_mode_e run_mode; uint32_t timeout; int ret = OK; DEBUGASSERT(cfg != NULL && cfg->src != SCG_SYSTEM_CLOCK_SRC_NONE); /* Get and convert Run mode from SMC to SCG defines */ run_mode = s32k1xx_get_runmode(); /* Check the current mode */ DEBUGASSERT(run_mode != SCG_SYSTEM_CLOCK_MODE_NONE); /* Update run mode configuration */ ret = s32k1xx_set_sysclk_cfg(run_mode, cfg); if (ret == OK) { /* Wait for system clock to transition. * * e10777: The SCG_RCCR[SCS] and SCG_HCCR[SCS] may have a corrupted * status during the interval when the system clock is switching. * Workaround: The SCS field should be read twice by the software to * ensure the system clock switch has completed. */ #if 1 /* Errata E10777 */ timeout = 10; #else timeout = 1; #endif do { timeout--; } while (s32k1xx_get_scgclk_source() != cfg->src && timeout > 0); if (timeout == 0) { ret = -ETIMEDOUT; } } return ret; } /**************************************************************************** * Name: s32k1xx_firc_config * * Description: * Configures FIRC module based on provided configuration. * * Input Parameters: * firccfg - Describes the desired FORC configuration. * * Returned Value: * Zero (OK) is returned a success; A negated errno value is returned on * any failure. * ****************************************************************************/ static int s32k1xx_firc_config(bool enable, const struct scg_firc_config_s *firccfg) { uint32_t regval; int32_t timeout; int ret = OK; DEBUGASSERT(firccfg != NULL); /* If clock is used by system, return error. */ regval = getreg32(S32K1XX_SCG_FIRCCSR); if ((regval & SCG_FIRCCSR_FIRCSEL) != 0) { ret = -EBUSY; } /* Disable the FIRC */ else { /* Clear LK bit field */ regval &= ~SCG_FIRCCSR_LK; putreg32(regval, S32K1XX_SCG_FIRCCSR); /* Disable monitor, disable clock and clear error. */ putreg32(SCG_FIRCCSR_FIRCERR, S32K1XX_SCG_FIRCCSR); } /* Configure FIRC. */ if (enable && (ret == OK)) { /* Now start to set up FIRC clock. */ /* Step 1. Setup dividers. */ regval = SCG_FIRCDIV_FIRCDIV1(firccfg->div1) | SCG_FIRCDIV_FIRCDIV2(firccfg->div2); putreg32(regval, S32K1XX_SCG_FIRCDIV); /* Step 2. Set FIRC configuration. */ if (firccfg->range == 0) { regval = 0; } else { regval = SCG_FIRCCFG_48MHZ; /* REVISIT: Also zero */ } putreg32(regval, S32K1XX_SCG_FIRCCFG); /* Step 3. Enable clock, config regulator and locking feature. */ regval = SCG_FIRCCSR_FIRCEN; if (!firccfg->regulator) { regval |= SCG_FIRCCSR_FIRCREGOFF; } if (firccfg->locked) { regval |= SCG_FIRCCSR_LK; } putreg32(regval, S32K1XX_SCG_FIRCCSR); /* Wait for FIRC to initialize */ for (timeout = FIRC_STABILIZATION_TIMEOUT; s32k1xx_get_fircfreq() == 0 && timeout > 0; timeout--) { } if (timeout <= 0) { ret = -ETIMEDOUT; } } return ret; } /**************************************************************************** * Name: s32k1xx_firc_clocksource * * Description: * Configure to the FIRC clock source. * * Input Parameters: * None * * Returned Value: * Zero (OK) is returned a success; A negated errno value is returned on * any failure. * ****************************************************************************/ static int s32k1xx_firc_clocksource(void) { struct scg_system_clock_config_s firccfg; int ret = OK; /* If the current system clock source is not FIRC: * 1. Enable FIRC (if it's not enabled) * 2. Switch to FIRC. */ if (s32k1xx_get_scgclk_source() != SCG_SYSTEM_CLOCK_SRC_FIRC) { /* If FIRC is not on, then FIRC is configured with the default * configuration */ if (s32k1xx_get_fircfreq() == 0) { ret = s32k1xx_firc_config(true, NULL); } /* FIRC is enabled, transition the system clock source to FIRC. */ if (ret == OK) { firccfg.src = SCG_SYSTEM_CLOCK_SRC_FIRC; firccfg.divcore = g_tmp_sysclk[TMP_FIRC_CLK][TMP_SYS_DIV]; firccfg.divbus = g_tmp_sysclk[TMP_FIRC_CLK][TMP_BUS_DIV]; firccfg.divslow = g_tmp_sysclk[TMP_FIRC_CLK][TMP_SLOW_DIV]; ret = s32k1xx_transition_systemclock(&firccfg); } } return ret; } /**************************************************************************** * Name: s32k1xx_sirc_config * * Description: * Configures SIRC module based on provided configuration. * * Input Parameters: * sirccfg - Describes the desired SIRC configuration. * * Returned Value: * Zero (OK) is returned a success; A negated errno value is returned on * any failure. * ****************************************************************************/ static int s32k1xx_sirc_config(bool enable, const struct scg_sirc_config_s *sirccfg) { uint32_t regval; uint32_t timeout; int ret = OK; DEBUGASSERT(sirccfg != NULL); /* If clock is used by system, return error. */ regval = getreg32(S32K1XX_SCG_SIRCCSR); if ((regval & SCG_SIRCCSR_SIRCSEL) != 0) { ret = -EBUSY; } /* Disable SIRC */ else { /* Clear LK bit field */ regval &= ~SCG_SIRCCSR_LK; putreg32(regval, S32K1XX_SCG_SIRCCSR); /* Disable monitor, disable clock and clear error. */ putreg32(0, S32K1XX_SCG_SIRCCSR); } /* Configure SIRC. */ if (enable && (ret == OK)) { /* Now start to set up SIRC clock. */ /* Step 1. Setup dividers. */ regval = SCG_SIRCDIV_SIRCDIV1(sirccfg->div1) | SCG_SIRCDIV_SIRCDIV2(sirccfg->div2); putreg32(regval, S32K1XX_SCG_SIRCDIV); /* Step 2. Set SIRC configuration: frequency range. */ if (sirccfg->range == SCG_SIRC_RANGE_HIGH) { regval = SCG_SIRCCFG_HIGHRANGE; } else { regval = SCG_SIRCCFG_LOWRANGE; } putreg32(regval, S32K1XX_SCG_SIRCCFG); /* Step 3. Set SIRC control: enable clock, configure source in STOP * and VLP modes, configure lock feature. */ regval = SCG_SIRCCSR_SIRCEN; if (sirccfg->stopmode) { regval |= SCG_SIRCCSR_SIRCSTEN; } if (sirccfg->lowpower) { regval |= SCG_SIRCCSR_SIRCLPEN; } if (sirccfg->locked) { regval |= SCG_SIRCCSR_LK; } putreg32(regval, S32K1XX_SCG_SIRCCSR); /* Wait for SIRC to initialize */ for (timeout = SIRC_STABILIZATION_TIMEOUT; s32k1xx_get_sircfreq() == 0 && timeout > 0; timeout--) { } if (timeout <= 0) { ret = -ETIMEDOUT; } } return ret; } #if defined(CONFIG_VLPR_STANDBY) || defined(CONFIG_VLPR_SLEEP) /**************************************************************************** * Name: s32k1xx_sirc_clocksource * * Description: * Configure to the SIRC clock source. * * Input Parameters: * None * * Returned Value: * Zero (OK) is returned a success; A negated errno value is returned on * any failure. * ****************************************************************************/ static int s32k1xx_sirc_clocksource(void) { struct scg_system_clock_config_s sirccfg; int ret = OK; /* If the current system clock source is not SIRC: * 1. Enable SIRC (if it's not enabled) * 2. Switch to SIRC. */ if (s32k1xx_get_scgclk_source() != SCG_SYSTEM_CLOCK_SRC_SIRC) { /* If SIRC is not on, then SIRC is configured with the default * configuration */ if (s32k1xx_get_sircfreq() == 0) { ret = s32k1xx_sirc_config(true, NULL); } /* SIRC is enabled, transition the system clock source to SIRC. */ if (ret == OK) { sirccfg.src = SCG_SYSTEM_CLOCK_SRC_SIRC; sirccfg.divcore = g_tmp_sysclk[TMP_SIRC_CLK][TMP_SYS_DIV]; sirccfg.divbus = g_tmp_sysclk[TMP_SIRC_CLK][TMP_BUS_DIV]; sirccfg.divslow = g_tmp_sysclk[TMP_SIRC_CLK][TMP_SLOW_DIV]; ret = s32k1xx_transition_systemclock(&sirccfg); } } return ret; } #endif /**************************************************************************** * Name: s32k1xx_sosc_config * * Description: * CConfigures SOSC module based on provided configuration. * * Input Parameters: * sosccfg - Describes the desired SOSC configuration. * * Returned Value: * Zero (OK) is returned a success; A negated errno value is returned on * any failure. * ****************************************************************************/ static int s32k1xx_sosc_config(bool enable, const struct scg_sosc_config_s *sosccfg) { uint32_t regval; uint32_t timeout; int ret = OK; DEBUGASSERT(sosccfg != NULL); /* If clock is used by system, return error. */ regval = getreg32(S32K1XX_SCG_SOSCCSR); if ((regval & SCG_SOSCCSR_SOSCSEL) != 0) { ret = -EBUSY; } /* Disable SOSC */ else { /* Clear LK bit field */ regval &= ~SCG_SOSCCSR_LK; putreg32(regval, S32K1XX_SCG_SOSCCSR); /* Disable monitor, disable clock and clear error. */ putreg32(SCG_SOSCCSR_SOSCERR, S32K1XX_SCG_SOSCCSR); } /* Configure the SOSC */ if (enable && (ret == OK)) { /* Now start to set up OSC clock */ /* Step 1. Setup dividers. */ regval = SCG_SOSCDIV_SOSCDIV1(sosccfg->div1) | SCG_SOSCDIV_SOSCDIV2(sosccfg->div2); putreg32(regval, S32K1XX_SCG_SOSCDIV); /* Step 2. Set OSC configuration. */ regval = SCG_SOSCCFG_RANGE(sosccfg->range); if (sosccfg->gain == SCG_SOSC_GAIN_HIGH) { regval |= SCG_SOSCCFG_HGO; } if (sosccfg->extref == SCG_SOSC_REF_OSC) { regval |= SCG_SOSCCFG_EREFS; } putreg32(regval, S32K1XX_SCG_SOSCCFG); /* Step 3. Enable clock, configure monitor, lock register. */ regval = SCG_SOSCCSR_SOSCEN; if (sosccfg->locked) { regval |= SCG_SOSCCSR_LK; } switch (sosccfg->mode) { case SCG_SOSC_MONITOR_DISABLE: { putreg32(regval, S32K1XX_SCG_SOSCCSR); } break; case SCG_SOSC_MONITOR_INT: { regval |= SCG_SOSCCSR_SOSCCM; putreg32(regval, S32K1XX_SCG_SOSCCSR); } break; case SCG_SOSC_MONITOR_RESET: { regval |= SCG_SOSCCSR_SOSCCM | SCG_SOSCCSR_SOSCCMRE; putreg32(regval, S32K1XX_SCG_SOSCCSR); } break; default: /* Invalid monitor mode */ DEBUGPANIC(); break; } /* Wait for System OSC to initialize */ for (timeout = SOSC_STABILIZATION_TIMEOUT; s32k1xx_get_soscfreq() == 0 && timeout > 0; timeout--) { } if (timeout <= 0) { ret = -ETIMEDOUT; } } return ret; } /**************************************************************************** * Name: s32k1xx_spll_config * * Description: * Configures SPLL module based on provided configuration. * * Input Parameters: * spllccfg - Describes the desired SPLL configuration. * * Returned Value: * Zero (OK) is returned a success; A negated errno value is returned on * any failure. * ****************************************************************************/ #ifdef CONFIG_S32K1XX_HAVE_SPLL static int s32k1xx_spll_config(bool enable, const struct scg_spll_config_s *spllcfg) { uint32_t regval; uint32_t srcfreq; uint32_t timeout; int ret = OK; DEBUGASSERT(spllcfg != NULL); /* If clock is used by system, return error. */ regval = getreg32(S32K1XX_SCG_SPLLCSR); if ((regval & SCG_SPLLCSR_SPLLSEL) != 0) { ret = -EBUSY; } /* Disable the SPLL. */ else { /* Clear LK bit field */ regval &= ~SCG_SPLLCSR_LK; putreg32(regval, S32K1XX_SCG_SPLLCSR); /* Disable monitor, disable clock and clear error. */ putreg32(SCG_SPLLCSR_SPLLERR, S32K1XX_SCG_SPLLCSR); } /* Configure SPLL */ if (enable && (ret == OK)) { /* Get clock source frequency. */ srcfreq = s32k1xx_get_soscfreq(); DEBUGASSERT(srcfreq != 0); /* Pre-divider checking. */ srcfreq /= spllcfg->prediv; DEBUGASSERT(srcfreq >= SCG_SPLL_REF_MIN && srcfreq <= SCG_SPLL_REF_MAX); /* Now start to set up PLL clock. */ regval = SCG_SPLLDIV_SPLLDIV1(spllcfg->div1) | SCG_SPLLDIV_SPLLDIV2(spllcfg->div2); putreg32(regval, S32K1XX_SCG_SPLLDIV); /* Step 2. Set PLL configuration. */ regval = SCG_SPLLCFG_PREDIV(spllcfg->prediv) | SCG_SPLLCFG_MULT(spllcfg->mult); putreg32(regval, S32K1XX_SCG_SPLLCFG); /* Step 3. * Enable clock, configure monitor, lock register. */ regval = SCG_SPLLCSR_SPLLEN; if (spllcfg->locked) { regval |= SCG_SPLLCSR_LK; } switch (spllcfg->mode) { case SCG_SPLL_MONITOR_DISABLE: { putreg32(regval, S32K1XX_SCG_SPLLCSR); } break; case SCG_SPLL_MONITOR_INT: { regval |= SCG_SPLLCSR_SPLLCM; putreg32(regval, S32K1XX_SCG_SPLLCSR); } break; case SCG_SPLL_MONITOR_RESET: { regval |= (SCG_SPLLCSR_SPLLCM | SCG_SPLLCSR_SPLLCMRE); putreg32(regval, S32K1XX_SCG_SPLLCSR); } break; default: /* Invalid monitor mode */ DEBUGPANIC(); break; } /* Wait for System PLL to initialize */ for (timeout = SPLL_STABILIZATION_TIMEOUT; s32k1xx_get_spllfreq() == 0 && timeout > 0; timeout--) { } if (timeout <= 0) { ret = -ETIMEDOUT; } } return ret; } #endif /**************************************************************************** * Name: s32k1xx_configure_scgmodules * * Description: * Configures all modules from SCG (SIRC, FIRC, SOSC and SPLL) * * Input Parameters: * None * * Returned Value: * Zero (OK) is returned a success; A negated errno value is returned on * any failure. * ****************************************************************************/ static int s32k1xx_configure_scgmodules(const struct scg_config_s *scgcfg) { struct scg_system_clock_config_s sysclkcfg; const struct scg_system_clock_config_s *next; int ret = OK; /* Configure all clock sources that are different from the current system * clock source FIRC (SIRC, SOSC, SPLL). */ ret = s32k1xx_sirc_config(scgcfg->sirc.initialize, &scgcfg->sirc); if (ret == OK) { ret = s32k1xx_sosc_config(scgcfg->sosc.initialize, &scgcfg->sosc); #ifdef CONFIG_S32K1XX_HAVE_SPLL if (ret == OK) { ret = s32k1xx_spll_config(scgcfg->spll.initialize, &scgcfg->spll); } #endif } /* Get the next system clock source */ switch (s32k1xx_get_runmode()) { case SCG_SYSTEM_CLOCK_MODE_RUN: { next = &scgcfg->clockmode.rccr; } break; case SCG_SYSTEM_CLOCK_MODE_VLPR: { next = &scgcfg->clockmode.vccr; } break; #ifdef CONFIG_S32K1XX_HAVE_HSRUN case SCG_SYSTEM_CLOCK_MODE_HSRUN: { next = &scgcfg->clockmode.hccr; } break; #endif default: DEBUGPANIC(); next = NULL; break; } if (ret == OK) { /* The current system clock source is FIRC. Verify whether the next * system clock source is FIRC. */ if (next->src == SCG_SYSTEM_CLOCK_SRC_FIRC) { /* If they are the same, search for a temporary system clock source * (use one of the following sources: SPLL, SOSC, SIRC). Assume * that a temporary clock is not found ret = -ENOENT. */ ret = -ENOENT; #ifdef CONFIG_S32K1XX_HAVE_SPLL /* SPLL is enabled */ if (scgcfg->spll.initialize && (ret == -ENOENT)) { sysclkcfg.src = SCG_SYSTEM_CLOCK_SRC_SYS_PLL; sysclkcfg.divcore = g_tmp_sysclk[TMP_SPLL_CLK][TMP_SYS_DIV]; sysclkcfg.divbus = g_tmp_sysclk[TMP_SPLL_CLK][TMP_BUS_DIV]; sysclkcfg.divslow = g_tmp_sysclk[TMP_SPLL_CLK][TMP_SLOW_DIV]; ret = s32k1xx_transition_systemclock(&sysclkcfg); } #endif /* SOSC is enabled and SPLL configuration for system clock source * is not valid */ if (scgcfg->sosc.initialize && (ret == -ENOENT)) { sysclkcfg.src = SCG_SYSTEM_CLOCK_SRC_SYS_OSC; sysclkcfg.divcore = g_tmp_sysclk[TMP_SOSC_CLK][TMP_SYS_DIV]; sysclkcfg.divbus = g_tmp_sysclk[TMP_SOSC_CLK][TMP_BUS_DIV]; sysclkcfg.divslow = g_tmp_sysclk[TMP_SOSC_CLK][TMP_SLOW_DIV]; ret = s32k1xx_transition_systemclock(&sysclkcfg); } /* SIRC is enabled and SOSC configuration for system clock * source is not valid */ if (scgcfg->sirc.initialize && (ret == -ENOENT)) { sysclkcfg.src = SCG_SYSTEM_CLOCK_SRC_SIRC; sysclkcfg.divcore = g_tmp_sysclk[TMP_SIRC_CLK][TMP_SYS_DIV]; sysclkcfg.divbus = g_tmp_sysclk[TMP_SIRC_CLK][TMP_BUS_DIV]; sysclkcfg.divslow = g_tmp_sysclk[TMP_SIRC_CLK][TMP_SLOW_DIV]; ret = s32k1xx_transition_systemclock(&sysclkcfg); } /* Transitioned to a temporary system clock source. */ if (ret == OK) { /* Configure the remaining clock source (FIRC). */ ret = s32k1xx_firc_config(scgcfg->firc.initialize, &scgcfg->firc); if (ret == OK) { /* Transition to the next system clock source. */ sysclkcfg.src = next->src; sysclkcfg.divcore = next->divcore; sysclkcfg.divbus = next->divbus; sysclkcfg.divslow = next->divslow; ret = s32k1xx_transition_systemclock(&sysclkcfg); } } } else { /* Transition to the next system clock source. */ sysclkcfg.src = next->src; sysclkcfg.divcore = next->divcore; sysclkcfg.divbus = next->divbus; sysclkcfg.divslow = next->divslow; ret = s32k1xx_transition_systemclock(&sysclkcfg); if (ret == OK) { /* Configure the remaining clock source (FIRC) */ ret = s32k1xx_firc_config(scgcfg->firc.initialize, &scgcfg->firc); } } } return ret; } /**************************************************************************** * Name: s32k1xx_scg_config * * Description: * Configure SCG clocking. * * Input Parameters: * scgcfg - Describes the new SCG clock configuration * * Returned Value: * Zero (OK) is returned a success; A negated errno value is returned on * any failure. * ****************************************************************************/ static int s32k1xx_scg_config(const struct scg_config_s *scgcfg) { uint32_t regval; int ret = OK; DEBUGASSERT(scgcfg != NULL); /* Configure a temporary system clock source: FIRC if enabled */ ret = s32k1xx_firc_clocksource(); if (ret == OK) { /* Configure clock sources from SCG */ ret = s32k1xx_configure_scgmodules(scgcfg); } if (ret == OK) { /* Configure RTC. */ #if 0 /* Not used */ if (scgcfg->rtc.initialize) { /* RTC Clock settings. */ g_rtc_clkin = scgcfg->rtc.clkin; } #endif /* Configure SCG ClockOut. */ if (scgcfg->clockout.initialize) { /* ClockOut settings. */ regval = getreg32(S32K1XX_SCG_CLKOUTCNFG); regval &= ~SCG_CLKOUTCNFG_CLKOUTSEL_MASK; regval |= SCG_CLKOUTCNFG_CLKOUTSEL(scgcfg->clockout.source); putreg32(regval, S32K1XX_SCG_CLKOUTCNFG); } /* Configure SCG clock modes. */ if (scgcfg->clockmode.initialize) { /* Configure SCG clock modes */ ret = s32k1xx_set_sysclk_cfg(SCG_SYSTEM_CLOCK_MODE_RUN, &scgcfg->clockmode.rccr); if (ret == OK) { ret = s32k1xx_set_sysclk_cfg(SCG_SYSTEM_CLOCK_MODE_VLPR, &scgcfg->clockmode.vccr); } #ifdef CONFIG_S32K1XX_HAVE_HSRUN if (ret == OK) { ret = s32k1xx_set_sysclk_cfg(SCG_SYSTEM_CLOCK_MODE_HSRUN, &scgcfg->clockmode.hccr); } #endif } } return ret; } /**************************************************************************** * Name: s32k1xx_sim_config * * Description: * Configure PCC clocking. * * Input Parameters: * simcfg - Describes the new SIM clock configuration * * Returned Value: * None. * ****************************************************************************/ static void s32k1xx_sim_config(const struct sim_clock_config_s *simcfg) { uint32_t regval; #ifdef CONFIG_S32K1XX_HAVE_QSPI int i; #endif DEBUGASSERT(simcfg != NULL); /* ClockOut settings. */ if (simcfg->clockout.initialize) { regval = getreg32(S32K1XX_SIM_CHIPCTL); regval &= ~(SIM_CHIPCTL_CLKOUTEN | SIM_CHIPCTL_CLKOUTDIV_MASK | SIM_CHIPCTL_CLKOUTSEL_MASK); if (simcfg->clockout.enable) { regval |= SIM_CHIPCTL_CLKOUTEN; } regval |= SIM_CHIPCTL_CLKOUTSEL(simcfg->clockout.source); regval |= SIM_CHIPCTL_CLKOUTDIV(simcfg->clockout.divider); putreg32(regval, S32K1XX_SIM_CHIPCTL); } /* Low Power Clock settings from SIM. */ if (simcfg->lpoclk.initialize) { regval = getreg32(S32K1XX_SIM_LPOCLKS); regval &= ~(SIM_LPOCLKS_LPO1KCLKEN | SIM_LPOCLKS_LPO32KCLKEN | SIM_LPOCLKS_LPOCLKSEL_MASK | SIM_LPOCLKS_RTCCLKSEL_MASK); if (simcfg->lpoclk.lpo1k) { regval |= SIM_LPOCLKS_LPO1KCLKEN; } if (simcfg->lpoclk.lpo32k) { regval |= SIM_LPOCLKS_LPO32KCLKEN; } regval |= SIM_LPOCLKS_LPOCLKSEL(simcfg->lpoclk.lpo_source); regval |= SIM_LPOCLKS_RTCCLKSEL(simcfg->lpoclk.rtc_source); putreg32(regval, S32K1XX_SIM_LPOCLKS); } /* Platform Gate Clock settings. */ if (simcfg->platgate.initialize) { regval = getreg32(S32K1XX_SIM_PLATCGC); regval &= ~(SIM_PLATCGC_CGCMSCM | SIM_PLATCGC_CGCMPU | SIM_PLATCGC_CGCDMA | SIM_PLATCGC_CGCERM | SIM_PLATCGC_CGCEIM); if (simcfg->platgate.mscm) { regval |= SIM_PLATCGC_CGCMSCM; } if (simcfg->platgate.mpu) { regval |= SIM_PLATCGC_CGCMPU; } if (simcfg->platgate.dma) { regval |= SIM_PLATCGC_CGCDMA; } if (simcfg->platgate.erm) { regval |= SIM_PLATCGC_CGCERM; } if (simcfg->platgate.eim) { regval |= SIM_PLATCGC_CGCEIM; } putreg32(regval, S32K1XX_SIM_PLATCGC); #ifdef CONFIG_S32K1XX_HAVE_QSPI regval = getreg32(S32K1XX_SIM_MISCTRL0); regval &= ~SIM_MISCTRL0_QSPI_CLK_SEL; if (simcfg->qspirefclk.refclk) { regval |= SIM_MISCTRL0_QSPI_CLK_SEL; } putreg32(regval, S32K1XX_SIM_MISCTRL0); #endif } #if 0 /* REVISIT: Not currently used */ /* TCLK Clock settings. */ if (simcfg->tclk.initialize) { for (i = 0; i < NUMBER_OF_TCLK_INPUTS; i++) { g_tclkfreq[i] = simcfg->tclk.tclkfreq[i]; } } #endif /* Debug trace Clock settings. */ if (simcfg->traceclk.initialize) { /* Disable divider. */ putreg32(0, S32K1XX_SIM_CLKDIV4); /* Configure trace source. */ regval = getreg32(S32K1XX_SIM_CHIPCTL); regval &= ~SIM_CHIPCTL_TRACECLK_SEL; if (simcfg->traceclk.source) { regval |= SIM_CHIPCTL_TRACECLK_SEL; } putreg32(regval, S32K1XX_SIM_CHIPCTL); if (simcfg->traceclk.enable) { regval = SIM_CLKDIV4_TRACEDIVEN | SIM_CLKDIV4_TRACEDIV(simcfg->traceclk.divider); if (simcfg->traceclk.fraction) { regval |= SIM_CLKDIV4_TRACEFRAC; } putreg32(regval, S32K1XX_SIM_CLKDIV4); } } } /**************************************************************************** * Name: s32k1xx_pmc_config * * Description: * Configure PMC clocking. * * Input Parameters: * pmccfg - Describes the new PMC clock configuration * * Returned Value: * None. * ****************************************************************************/ static void s32k1xx_pmc_config(const struct pmc_config_s *pmccfg) { uint8_t regval; DEBUGASSERT(pmccfg != NULL); /* Low Power Clock settings from PMC. */ if (pmccfg->lpoclk.initialize) { /* Enable/disable the low power oscillator. */ regval = getreg8(S32K1XX_PMC_REGSC); if (pmccfg->lpoclk.enable) { regval &= ~PMC_REGSC_LPODIS; } else { regval |= PMC_REGSC_LPODIS; } /* Enable Biasing (needed for VLPR mode, no effect in RUN mode) */ regval |= PMC_REGSC_BIASEN; putreg8(regval, S32K1XX_PMC_REGSC); /* Write trimming value. */ putreg8(pmccfg->lpoclk.trim & PMC_LPOTRIM_MASK, S32K1XX_PMC_LPOTRIM); } } /**************************************************************************** * Name: s32k1xx_allow_vlprmode * * Description: * allow the very low power run mode. * * Input Parameters: * allow - true if allowed, false otherwise. * * Returned Value: * none. * ****************************************************************************/ void s32k1xx_allow_vlprmode(bool allow) { uint32_t regval; /* get the SMC_PMPROT register */ regval = getreg32(S32K1XX_SMC_PMPROT); /* mask the AVLP bit */ regval &= ~SMC_PMPROT_AVLP; /* set the new bit */ regval |= (allow << SMC_PMPROT_AVLP_SHIFT); /* set the registervalue */ putreg32(regval, S32K1XX_SMC_PMPROT); } /**************************************************************************** * Name: up_pm_notify * * Description: * Notify the driver of new power state. This callback is called after * all drivers have had the opportunity to prepare for the new power state. * * Input Parameters: * * cb - Returned to the driver. The driver version of the callback * structure may include additional, driver-specific state data at * the end of the structure. * * pmstate - Identifies the new PM state * * Returned Value: * None - The driver already agreed to transition to the low power * consumption state when when it returned OK to the prepare() call. * ****************************************************************************/ #ifdef CONFIG_PM static void up_pm_notify(struct pm_callback_s *cb, int domain, enum pm_state_e pmstate) { int return_value; /* check if the transition is from the IDLE domain to the NORMAL domain */ if (pm_querystate(PM_IDLE_DOMAIN) == PM_IDLE && pmstate == PM_NORMAL) { /* return */ return; } /* check what the new power state is */ switch (pmstate) { /* if it needs to be set to RUN mode */ case(PM_NORMAL): { /* Logic for PM_NORMAL goes here */ /* change the microcontroller to RUN mode */ /* and wait until in RUN mode */ return_value = (int)s32k1xx_set_runmode(SCG_SYSTEM_CLOCK_MODE_RUN); /* check for debug assertion */ DEBUGASSERT(return_value != (int)SCG_SYSTEM_CLOCK_MODE_NONE); /* enable all clock sources again if needed */ /* these could be the FIRC, PPL, and SOSC */ /* check if the FIRC was enabled and * it is not the system clock source */ if (g_initial_clkconfig.scg.firc.initialize && (s32k1xx_get_scgclk_source() != SCG_SYSTEM_CLOCK_SRC_SIRC)) { /* enable FIRC */ return_value = s32k1xx_firc_config(true, &g_initial_clkconfig.scg.firc); DEBUGASSERT(!return_value); } /* check if the FIRC needs to be disabled and if it is enabled */ else if ((!(g_initial_clkconfig.scg.firc.initialize)) && (s32k1xx_get_srcfreq(SCG_SYSTEM_CLOCK_SRC_FIRC))) { /* disable FIRC */ return_value = s32k1xx_firc_config(false, &g_initial_clkconfig.scg.firc); DEBUGASSERT(!return_value); } /* check if the SOSC was enabled and * it is not the system clock source */ if (g_initial_clkconfig.scg.sosc.initialize && (s32k1xx_get_scgclk_source() != SCG_SYSTEM_CLOCK_SRC_SYS_OSC) && (s32k1xx_get_scgclk_source() != SCG_SYSTEM_CLOCK_SRC_SYS_PLL)) { /* enable SOSC */ return_value = s32k1xx_sosc_config(true, &g_initial_clkconfig.scg.sosc); DEBUGASSERT(!return_value); } /* check if the SOSC needs to be disabled and if it is enabled */ else if ((!(g_initial_clkconfig.scg.sosc.initialize)) && (s32k1xx_get_srcfreq(SCG_SYSTEM_CLOCK_SRC_SYS_OSC))) { /* disable SOSC */ return_value = s32k1xx_sosc_config(false, &g_initial_clkconfig.scg.sosc); DEBUGASSERT(!return_value); } /* check if the SPLL was enabled and * it is not the system clock source */ if (g_initial_clkconfig.scg.spll.initialize && (s32k1xx_get_scgclk_source() != SCG_SYSTEM_CLOCK_SRC_SYS_OSC) && (s32k1xx_get_scgclk_source() != SCG_SYSTEM_CLOCK_SRC_SYS_PLL)) { /* enable SPLL */ return_value = s32k1xx_spll_config(true, &g_initial_clkconfig.scg.spll); DEBUGASSERT(!return_value); } /* check if the SPLL needs to be disabled and if it is enabled */ else if ((!(g_initial_clkconfig.scg.spll.initialize)) && (s32k1xx_get_srcfreq(SCG_SYSTEM_CLOCK_SRC_SYS_PLL))) { /* disable SPLL */ return_value = s32k1xx_spll_config(false, &g_initial_clkconfig.scg.spll); DEBUGASSERT(!return_value); } /* check if the RCCR clock source is enabled */ if (s32k1xx_get_srcfreq(g_initial_clkconfig.scg.clockmode.rccr.src) != 0) { /* change the system clock back to the configured clock */ /* and wait until clock changed */ if (s32k1xx_transition_systemclock( &g_initial_clkconfig.scg.clockmode.rccr)) { /* error */ DEBUGPANIC(); } } /* if it is 0 */ else { /* error */ DEBUGPANIC(); } /* calculate the new clock ticks */ up_timer_initialize(); } break; case(PM_IDLE): { /* Logic for PM_IDLE goes here */ } break; /* if it needs to be set to VLPR mode */ case(PM_STANDBY): { /* Logic for PM_STANDBY goes here */ #ifdef CONFIG_RUN_STANDBY /* change the microcontroller to RUN mode */ /* and wait until in RUN mode */ return_value = (int)s32k1xx_set_runmode(SCG_SYSTEM_CLOCK_MODE_RUN); DEBUGASSERT(return_value != (int)SCG_SYSTEM_CLOCK_MODE_NONE); /* enable all clock sources again if needed */ /* these could be the FIRC, PPL, and SOSC */ /* check if the FIRC was enabled and * it is not the system clock source */ if (g_initial_clkconfig.scg.firc.initialize && (s32k1xx_get_scgclk_source() != SCG_SYSTEM_CLOCK_SRC_SIRC)) { /* enable FIRC */ return_value = s32k1xx_firc_config(true, &g_initial_clkconfig.scg.firc); DEBUGASSERT(!return_value); } /* check if the FIRC needs to be disabled and if it is enabled */ else if ((!(g_initial_clkconfig.scg.firc.initialize)) && (s32k1xx_get_srcfreq(SCG_SYSTEM_CLOCK_SRC_FIRC))) { /* disable FIRC */ return_value = s32k1xx_firc_config(false, &g_initial_clkconfig.scg.firc); DEBUGASSERT(!return_value); } /* check if the SOSC was enabled and * it is not the system clock source */ if (g_initial_clkconfig.scg.sosc.initialize && (s32k1xx_get_scgclk_source() != SCG_SYSTEM_CLOCK_SRC_SYS_OSC) && (s32k1xx_get_scgclk_source() != SCG_SYSTEM_CLOCK_SRC_SYS_PLL)) { /* enable SOSC */ return_value = s32k1xx_sosc_config(true, &g_initial_clkconfig.scg.sosc); DEBUGASSERT(!return_value); } /* check if the SOSC needs to be disabled and if it is enabled */ else if ((!(g_initial_clkconfig.scg.sosc.initialize)) && (s32k1xx_get_srcfreq(SCG_SYSTEM_CLOCK_SRC_SYS_OSC))) { /* disable SOSC */ return_value = s32k1xx_sosc_config(false, &g_initial_clkconfig.scg.sosc); DEBUGASSERT(!return_value); } /* check if the SPLL was enabled and * it is not the system clock source */ if (g_initial_clkconfig.scg.spll.initialize && (s32k1xx_get_scgclk_source() != SCG_SYSTEM_CLOCK_SRC_SYS_OSC) && (s32k1xx_get_scgclk_source() != SCG_SYSTEM_CLOCK_SRC_SYS_PLL)) { /* enable SPLL */ return_value = s32k1xx_spll_config(true, &g_initial_clkconfig.scg.spll); DEBUGASSERT(!return_value); } /* check if the SPLL needs to be disabled and if it is enabled */ else if ((!(g_initial_clkconfig.scg.spll.initialize)) && (s32k1xx_get_srcfreq(SCG_SYSTEM_CLOCK_SRC_SYS_PLL))) { /* disable SPLL */ return_value = s32k1xx_spll_config(false, &g_initial_clkconfig.scg.spll); DEBUGASSERT(!return_value); } /* check if the RCCR clock source is enabled */ if (s32k1xx_get_srcfreq(g_initial_clkconfig.scg.clockmode.rccr.src) != 0) { /* change the system clock back to the configured clock */ /* and wait until clock changed */ if (s32k1xx_transition_systemclock( &g_initial_clkconfig.scg.clockmode.rccr)) { /* error */ DEBUGPANIC(); } } /* if it is 0 */ else { /* error */ DEBUGPANIC(); } #endif /* CONFIG_RUN_STANDBY */ #ifdef CONFIG_VLPR_STANDBY /* set the system clock to the SIRC 8MHz freq */ /* this freq will change to the predefined vccr settings * when the mode change occures */ /* and wait until system clock changed */ return_value = s32k1xx_sirc_clocksource(); DEBUGASSERT(!return_value); /* disable the other clock sources if not already disabled */ /* these are the FIRC, PPL, and SOSC */ /* check if the SPLL is enabled */ if (s32k1xx_get_spllfreq() != 0) { /* disable SPLL */ return_value = s32k1xx_spll_config(false, &g_initial_clkconfig.scg.spll); DEBUGASSERT(!return_value); } /* check if the SOSC is enabled */ if (s32k1xx_get_soscfreq() != 0) { /* disable SOSC */ return_value = s32k1xx_sosc_config(false, &g_initial_clkconfig.scg.sosc); DEBUGASSERT(!return_value); } /* check if the FIRC is enabled */ if (s32k1xx_get_fircfreq() != 0) { /* disable FIRC */ return_value = s32k1xx_firc_config(false, &g_initial_clkconfig.scg.firc); DEBUGASSERT(!return_value); } #ifdef CONFIG_ARCH_CHIP_S32K11X /* TODO make sure CMU is gated? (only for S32k11x) */ #error Make sure CMU is gated #endif /* change the microcontroller to VLPR mode */ /* and wait until it is in that runmode */ return_value = (int)s32k1xx_set_runmode(SCG_SYSTEM_CLOCK_MODE_VLPR); DEBUGASSERT(return_value != (int)SCG_SYSTEM_CLOCK_MODE_NONE); #endif /* CONFIG_VLPR_STANDBY */ /* calculate the new clock ticks */ up_timer_initialize(); } break; case(PM_SLEEP): { /* Logic for PM_SLEEP goes here */ #ifdef CONFIG_RUN_SLEEP /* change the microcontroller to RUN mode */ /* and wait until in RUN mode */ return_value = (int)s32k1xx_set_runmode(SCG_SYSTEM_CLOCK_MODE_RUN); DEBUGASSERT(return_value != (int)SCG_SYSTEM_CLOCK_MODE_NONE); /* enable all clock sources again if needed */ /* these could be the FIRC, PPL, and SOSC */ /* check if the FIRC was enabled * and it is not the system clock source */ if (g_initial_clkconfig.scg.firc.initialize && (s32k1xx_get_scgclk_source() != SCG_SYSTEM_CLOCK_SRC_SIRC)) { /* enable FIRC */ return_value = s32k1xx_firc_config(true, &g_initial_clkconfig.scg.firc); DEBUGASSERT(!return_value); } /* check if the FIRC needs to be disabled and if it is enabled */ else if ((!(g_initial_clkconfig.scg.firc.initialize)) && (s32k1xx_get_srcfreq(SCG_SYSTEM_CLOCK_SRC_FIRC))) { /* disable FIRC */ return_value = s32k1xx_firc_config(false, &g_initial_clkconfig.scg.firc); DEBUGASSERT(!return_value); } /* check if the SOSC was enabled * and it is not the system clock source */ if (g_initial_clkconfig.scg.sosc.initialize && (s32k1xx_get_scgclk_source() != SCG_SYSTEM_CLOCK_SRC_SYS_OSC) && (s32k1xx_get_scgclk_source() != SCG_SYSTEM_CLOCK_SRC_SYS_PLL)) { /* enable SOSC */ return_value = s32k1xx_sosc_config(true, &g_initial_clkconfig.scg.sosc); DEBUGASSERT(!return_value); } /* check if the SOSC needs to be disabled and if it is enabled */ else if ((!(g_initial_clkconfig.scg.sosc.initialize)) && (s32k1xx_get_srcfreq(SCG_SYSTEM_CLOCK_SRC_SYS_OSC))) { /* disable SOSC */ return_value = s32k1xx_sosc_config(false, &g_initial_clkconfig.scg.sosc); DEBUGASSERT(!return_value); } /* check if the SPLL was enabled * and it is not the system clock source */ if (g_initial_clkconfig.scg.spll.initialize && (s32k1xx_get_scgclk_source() != SCG_SYSTEM_CLOCK_SRC_SYS_OSC) && (s32k1xx_get_scgclk_source() != SCG_SYSTEM_CLOCK_SRC_SYS_PLL)) { /* enable SPLL */ return_value = s32k1xx_spll_config(true, &g_initial_clkconfig.scg.spll); DEBUGASSERT(!return_value); } /* check if the SPLL needs to be disabled and if it is enabled */ else if ((!(g_initial_clkconfig.scg.spll.initialize)) && (s32k1xx_get_srcfreq(SCG_SYSTEM_CLOCK_SRC_SYS_PLL))) { /* disable SPLL */ return_value = s32k1xx_spll_config(false, &g_initial_clkconfig.scg.spll); DEBUGASSERT(!return_value); } /* check if the RCCR clock source is enabled */ if (s32k1xx_get_srcfreq(g_initial_clkconfig.scg.clockmode.rccr.src) != 0) { /* change the system clock back to the configured clock */ /* and wait until clock changed */ if (s32k1xx_transition_systemclock( &g_initial_clkconfig.scg.clockmode.rccr)) { /* error */ DEBUGPANIC(); } } /* if it is 0 */ else { /* error */ DEBUGPANIC(); } #endif /* CONFIG_RUN_SLEEP */ #ifdef CONFIG_VLPR_SLEEP /* set the system clock to the SIRC 8MHz freq */ /* this freq will change to the predefined vccr settings * when the mode change occures */ /* and wait until system clock changed */ return_value = s32k1xx_sirc_clocksource(); DEBUGASSERT(!return_value); /* disable the other clock sources if not already disabled */ /* these are the FIRC, PPL, and SOSC */ /* check if the SPLL is enabled */ if (s32k1xx_get_spllfreq() != 0) { /* disable SPLL */ return_value = s32k1xx_spll_config(false, &g_initial_clkconfig.scg.spll); DEBUGASSERT(!return_value); } /* check if the SOSC is enabled */ if (s32k1xx_get_soscfreq() != 0) { /* disable SOSC */ return_value = s32k1xx_sosc_config(false, &g_initial_clkconfig.scg.sosc); DEBUGASSERT(!return_value); } /* check if the FIRC is enabled */ if (s32k1xx_get_fircfreq() != 0) { /* disable FIRC */ return_value = s32k1xx_firc_config(false, &g_initial_clkconfig.scg.firc); DEBUGASSERT(!return_value); } #ifdef CONFIG_ARCH_CHIP_S32K11X /* TODO make sure CMU is gated? (only for S32k11x) */ #error Make sure CMU is gated #endif /* change the microcontroller to VLPR mode */ /* and wait until it is in that runmode */ return_value = (int)s32k1xx_set_runmode(SCG_SYSTEM_CLOCK_MODE_VLPR); DEBUGASSERT(return_value != (int)SCG_SYSTEM_CLOCK_MODE_NONE); #endif /* CONFIG_VLPR_SLEEP */ /* calculate the new clock ticks */ up_timer_initialize(); } break; default: /* Should not get here */ break; } } #endif /**************************************************************************** * Name: up_pm_prepare * * Description: * Request the driver to prepare for a new power state. This is a warning * that the system is about to enter into a new power state. The driver * should begin whatever operations that may be required to enter power * state. The driver may abort the state change mode by returning a * non-zero value from the callback function. * * Input Parameters: * * cb - Returned to the driver. The driver version of the callback * structure may include additional, driver-specific state data at * the end of the structure. * * pmstate - Identifies the new PM state * * Returned Value: * Zero - (OK) means the event was successfully processed and that the * driver is prepared for the PM state change. * * Non-zero - means that the driver is not prepared to perform the tasks * needed achieve this power setting and will cause the state * change to be aborted. NOTE: The prepare() method will also * be called when reverting from lower back to higher power * consumption modes (say because another driver refused a * lower power state change). Drivers are not permitted to * return non-zero values when reverting back to higher power * consumption modes! * * ****************************************************************************/ #ifdef CONFIG_PM static int up_pm_prepare(struct pm_callback_s *cb, int domain, enum pm_state_e pmstate) { /* Logic to prepare for a reduced power state goes here. */ return OK; } #endif /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: s32k1xx_get_runmode * * Description: * Get the current running mode. * * Input Parameters: * None * * Returned Value: * The current running mode. * ****************************************************************************/ enum scg_system_clock_mode_e s32k1xx_get_runmode(void) { enum scg_system_clock_mode_e mode; /* Get the current running mode */ switch (getreg32(S32K1XX_SMC_PMSTAT) & SMC_PMSTAT_PMSTAT_MASK) { /* Run mode */ case SMC_PMSTAT_PMSTAT_RUN: mode = SCG_SYSTEM_CLOCK_MODE_RUN; break; /* Very low power run mode */ case SMC_PMSTAT_PMSTAT_VLPR: mode = SCG_SYSTEM_CLOCK_MODE_VLPR; break; /* High speed run mode */ case SMC_PMSTAT_PMSTAT_HSRUN: mode = SCG_SYSTEM_CLOCK_MODE_HSRUN; break; /* This should never happen - core has to be in some run mode to * execute code */ case SMC_PMSTAT_PMSTAT_VLPS: default: mode = SCG_SYSTEM_CLOCK_MODE_NONE; break; } return mode; } /**************************************************************************** * Name: s32k1xx_set_runmode * * Description: * Set the running mode. * * Input Parameters: * next_run_mode - The next running mode. * * Returned Value: * The current running mode. * ****************************************************************************/ enum scg_system_clock_mode_e s32k1xx_set_runmode(enum scg_system_clock_mode_e next_run_mode) { enum scg_system_clock_mode_e mode; /* get the current run mode */ mode = s32k1xx_get_runmode(); uint32_t regval; /* check if the current runmode is not the same as the next runmode */ if (mode != next_run_mode) { /* check what the next mode is */ switch (next_run_mode) { /* in case of the RUN mode */ /* it will use the clock configuration from S32K1XX_SCG_RCCR */ case SCG_SYSTEM_CLOCK_MODE_RUN: /* check if in VLPR mode */ if (mode == SCG_SYSTEM_CLOCK_MODE_VLPR) { /* get the SMC_PMCTRL register */ regval = getreg32(S32K1XX_SMC_PMCTRL); /* mask the RUNM bits */ regval &= ~SMC_PMCTRL_RUNM_MASK; /* change the mode to RUN mode */ regval |= SMC_PMCTRL_RUNM_RUN; /* write the register */ putreg32(regval, S32K1XX_SMC_PMCTRL); /* wait until it is in RUN mode */ while (s32k1xx_get_runmode() != SCG_SYSTEM_CLOCK_MODE_RUN); } break; /* in case of the VLPR mode */ /* it will use the clock configuration from S32K1XX_SCG_VCCR */ case SCG_SYSTEM_CLOCK_MODE_VLPR: /* check if in RUN mode and VLPR mode is allowed */ if ((mode == SCG_SYSTEM_CLOCK_MODE_RUN) && (getreg32(S32K1XX_SMC_PMPROT) & SMC_PMPROT_AVLP)) { /* get the SMC_PMCTRL register */ regval = getreg32(S32K1XX_SMC_PMCTRL); /* mask the RUNM bits */ regval &= ~SMC_PMCTRL_RUNM_MASK; /* change the mode to VLPR mode */ regval |= SMC_PMCTRL_RUNM_VLPR; /* write the register */ putreg32(regval, S32K1XX_SMC_PMCTRL); /* wait until it is in VLPR mode */ while (s32k1xx_get_runmode() != SCG_SYSTEM_CLOCK_MODE_VLPR); } break; /* others are not implemented */ default: break; } /* get the current run mode */ mode = s32k1xx_get_runmode(); } /* return the mode */ return mode; } /**************************************************************************** * Name: s32k1xx_clockconfig * * Description: * Called to initialize the S32K1XX. This does whatever setup is needed * to put the MCU in a usable state. This includes the initialization of * clocking using the settings in board.h. This function also performs * other low-level chip as necessary. * * Input Parameters: * clkcfg - Describes the new clock configuration * * Returned Value: * Zero (OK) is returned a success; A negated errno value is returned on * any failure. * ****************************************************************************/ int s32k1xx_clockconfig(const struct clock_configuration_s *clkcfg) { int ret; DEBUGASSERT(clkcfg != NULL); /* Set SCG configuration */ ret = s32k1xx_scg_config(&clkcfg->scg); if (ret >= 0) { /* Allow the VLPR mode */ s32k1xx_allow_vlprmode(true); /* Set PCC configuration */ s32k1xx_periphclocks(clkcfg->pcc.count, clkcfg->pcc.pclks); /* Set SIM configuration */ s32k1xx_sim_config(&clkcfg->sim); /* Set PMC configuration */ s32k1xx_pmc_config(&clkcfg->pmc); } return ret; } /**************************************************************************** * Name: s32k1xx_clock_pm_register * * Description: * This function is called after OS and PM init in order to register to * receive power management event callbacks. * * Input Parameters: * None * * Returned Values: * None * ****************************************************************************/ #ifdef CONFIG_PM void s32k1xx_clock_pm_register(void) { /* Register to receive power management callbacks */ pm_register(&g_clock_pmcb); } #endif /**************************************************************************** * Name: s32k1xx_get_coreclk * * Description: * Return the current value of the CORE clock frequency. * * Input Parameters: * None * * Returned Values: * The current value of the CORE clock frequency. Zero is returned on any * failure. * ****************************************************************************/ uint32_t s32k1xx_get_coreclk(void) { uint32_t coreclk = 0; uint32_t regval; uint32_t divider; #ifdef CONFIG_S32K1XX_HAVE_SPLL uint32_t prediv; uint32_t mult; #endif /* Get the core clock divider */ regval = getreg32(S32K1XX_SCG_CSR); divider = ((regval & SCG_CSR_DIVCORE_MASK) >> SCG_CSR_DIVCORE_SHIFT) + 1; /* Handle according to the selection clock source */ switch (regval & SCG_CSR_SCS_MASK) { case SCG_CSR_SCS_SOSC: /* System OSC */ coreclk = BOARD_XTAL_FREQUENCY; break; case SCG_CSR_SCS_SIRC: /* Slow IRC */ regval = getreg32(S32K1XX_SCG_SIRCCFG) & SCG_SIRCCFG_RANGE; if (regval == SCG_SIRCCFG_LOWRANGE) { /* Slow IRC low range clock (2 MHz) */ return 0; } /* Slow IRC high range clock (8 MHz ) */ coreclk = SCG_SIRC_HIGHRANGE_FREQUENCY; break; case SCG_CSR_SCS_FIRC: /* Fast IRC */ regval = getreg32(S32K1XX_SCG_FIRCCFG) & SCG_FIRCCFG_RANGE; if (regval != SCG_FIRCCFG_48MHZ) { return 0; } /* Fast IRC is trimmed to 48 MHz */ coreclk = SCG_FIRC_FREQUENCY0; break; #ifdef CONFIG_S32K1XX_HAVE_SPLL case SCG_CSR_SPLL_FIRC: /* System PLL */ /* Coreclock = Fxtal * mult / (2 * prediv) */ regval = getreg32(S32K1XX_SCG_SPLLCFG); prediv = ((regval & SCG_SPLLCFG_PREDIV_MASK) >> SCG_SPLLCFG_PREDIV_SHIFT) + 1; mult = ((regval & SCG_SPLLCFG_MULT_MASK) >> SCG_SPLLCFG_MULT_SHIFT) + 16; coreclk = ((BOARD_XTAL_FREQUENCY / 2) * mult) / prediv; break; #endif default: return 0; } return coreclk / divider; } /**************************************************************************** * Name: s32k1xx_get_sysclk * * Description: * Return the current value of an SCG system clock frequency, these clocks * are used for core, platform, external and bus clock domains.. * * Input Parameters: * type - Identifies the system clock of interest * * Returned Values: * The current value of the system clock frequency. Zero is returned on * any failure. * ****************************************************************************/ uint32_t s32k1xx_get_sysclk(enum scg_system_clock_type_e type) { enum scg_system_clock_src_e clksrc; uint32_t regval; uint32_t divider; uint32_t freq; /* Get the SCG current system clock source */ clksrc = (enum scg_system_clock_src_e)s32k1xx_get_scgclk_source(); freq = s32k1xx_get_srcfreq(clksrc); /* Adjust to count for the code divider */ regval = getreg32(S32K1XX_SCG_CSR); divider = ((regval & SCG_CSR_DIVCORE_MASK) >> SCG_CSR_DIVCORE_SHIFT) + 1; freq /= divider; /* Handle additional dividers for the clock type */ switch (type) { case SCG_SYSTEM_CLOCK_CORE: break; case SCG_SYSTEM_CLOCK_BUS: divider = ((regval & SCG_CSR_DIVBUS_MASK) >> SCG_CSR_DIVBUS_SHIFT) + 1; freq /= divider; break; case SCG_SYSTEM_CLOCK_SLOW: divider = ((regval & SCG_CSR_DIVSLOW_MASK) >> SCG_CSR_DIVSLOW_SHIFT) + 1; freq /= divider; break; default: freq = 0; break; } return freq; } /**************************************************************************** * Name: s32k1xx_get_asnchfreq * * Description: * Gets SCG asynchronous clock frequency from a clock source. * * Input Parameters: * clksrc - The requested clock source. * type - The requested clock type. * * Returned Value: * The frequency of the requested asynchronous clock source. * ****************************************************************************/ uint32_t s32k1xx_get_asnchfreq(enum clock_names_e clksrc, enum scg_async_clock_type_e type) { uint32_t regval; uint32_t freq = 0; uint32_t div = 0; switch (type) { case SCG_ASYNC_CLOCK_DIV1: { switch (clksrc) { case FIRC_CLK: { freq = s32k1xx_get_fircfreq(); regval = getreg32(S32K1XX_SCG_FIRCDIV); div = (regval & SCG_FIRCDIV_FIRCDIV1_MASK) >> SCG_FIRCDIV_FIRCDIV1_SHIFT; } break; case SIRC_CLK: { freq = s32k1xx_get_sircfreq(); regval = getreg32(S32K1XX_SCG_SIRCDIV); div = (regval & SCG_SIRCDIV_SIRCDIV1_MASK) >> SCG_SIRCDIV_SIRCDIV1_SHIFT; } break; case SOSC_CLK: { freq = s32k1xx_get_soscfreq(); regval = getreg32(S32K1XX_SCG_SOSCDIV); div = (regval & SCG_SOSCDIV_SOSCDIV1_MASK) >> SCG_SOSCDIV_SOSCDIV1_SHIFT; } break; #ifdef CONFIG_S32K1XX_HAVE_SPLL case SPLL_CLK: { freq = s32k1xx_get_spllfreq(); regval = getreg32(S32K1XX_SCG_SPLLDIV); div = (regval & SCG_SPLLDIV_SPLLDIV1_MASK) >> SCG_SPLLDIV_SPLLDIV1_SHIFT; } break; #endif default: { /* Invalid clock source type */ freq = 0; DEBUGPANIC(); } break; } } break; case SCG_ASYNC_CLOCK_DIV2: { switch (clksrc) { case FIRC_CLK: { freq = s32k1xx_get_fircfreq(); regval = getreg32(S32K1XX_SCG_FIRCDIV); div = (regval & SCG_FIRCDIV_FIRCDIV2_MASK) >> SCG_FIRCDIV_FIRCDIV2_SHIFT; } break; case SIRC_CLK: { freq = s32k1xx_get_sircfreq(); regval = getreg32(S32K1XX_SCG_SIRCDIV); div = (regval & SCG_SIRCDIV_SIRCDIV2_MASK) >> SCG_SIRCDIV_SIRCDIV2_SHIFT; } break; case SOSC_CLK: { freq = s32k1xx_get_soscfreq(); regval = getreg32(S32K1XX_SCG_SOSCDIV); div = (regval & SCG_SOSCDIV_SOSCDIV2_MASK) >> SCG_SOSCDIV_SOSCDIV2_SHIFT; } break; #ifdef CONFIG_S32K1XX_HAVE_SPLL case SPLL_CLK: { freq = s32k1xx_get_spllfreq(); regval = getreg32(S32K1XX_SCG_SPLLDIV); div = (regval & SCG_SPLLDIV_SPLLDIV2_MASK) >> SCG_SPLLDIV_SPLLDIV2_SHIFT; } break; #endif default: { /* Invalid clock source type */ freq = 0; DEBUGPANIC(); } break; } } break; default: /* Invalid async clock source */ freq = 0; DEBUGPANIC(); break; } if (div != 0) { freq = (freq >> (div - 1)); } else /* Output disabled. */ { freq = 0; } return freq; }