nuttx/arch/arm/src/s32k1xx/s32k1xx_clockconfig.c
2022-08-19 06:01:30 -07:00

2894 lines
77 KiB
C

/****************************************************************************
* arch/arm/src/s32k1xx/s32k1xx_clockconfig.c
*
* Copyright (C) 2019 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* 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 <nuttx/config.h>
#include <stdint.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/power/pm.h>
#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 <arch/board/board.h> /* 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;
}