| /**************************************************************************** |
| * arch/arm/src/s32k3xx/s32k3xx_clockconfig.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /* Copyright 2022 NXP */ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <stdint.h> |
| #include <assert.h> |
| #include <debug.h> |
| |
| #include <nuttx/arch.h> |
| |
| #include "arm_internal.h" |
| |
| #include "hardware/s32k3xx_mc_me.h" |
| #include "hardware/s32k3xx_mc_cgm.h" |
| #include "hardware/s32k3xx_firc.h" |
| #include "hardware/s32k3xx_sirc.h" |
| #include "hardware/s32k3xx_fxosc.h" |
| #include "hardware/s32k3xx_sxosc.h" |
| #include "hardware/s32k3xx_pll.h" |
| #include "s32k3xx_pin.h" |
| #include "hardware/s32k344_pinmux.h" |
| #include "s32k3xx_periphclocks.h" |
| #include "s32k3xx_clockconfig.h" |
| |
| #include <arch/board/board.h> /* Include last. May have dependencies */ |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: s32k3xx_get_scs_clk_source |
| * |
| * Description: |
| * Gets SCS current system clock source |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * 1 equals PHI1 0 equals FIRC |
| * |
| ****************************************************************************/ |
| |
| static inline uint32_t s32k3xx_get_scs_clk_source(void) |
| { |
| return ((getreg32(S32K3XX_MC_CGM_MUX_0_CSC) |
| & MC_CGM_MUX_0_CSC_SELCTL_MASK) == |
| MC_CGM_MUX_CSC_SELCTL_PLL_PHI0_CLK); |
| } |
| |
| /**************************************************************************** |
| * Name: s32k3xx_get_fxoscfreq |
| * |
| * Description: |
| * Gets FXOSC clock frequency (FXOSC). |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * The FXOSC frequency. Zero is returned if the FXOSC is not stable. |
| * |
| ****************************************************************************/ |
| |
| static uint32_t s32k3xx_get_fxoscfreq(void) |
| { |
| /* Check if the FXOSC is stable */ |
| |
| if ((getreg32(S32K3XX_FXOSC_STAT) & FXOSC_STAT_OSC_STAT_ON) != 0) |
| { |
| return BOARD_XTAL_FREQUENCY; |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: s32k3xx_get_sircfreq |
| * |
| * Description: |
| * Gets Slow IRC clock frequency (SIRC). |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * The SIRC frequency. Zero is returned if the SIRC is invalid. |
| * |
| ****************************************************************************/ |
| |
| static uint32_t s32k3xx_get_sircfreq(void) |
| { |
| /* Check if the SIRC is valid */ |
| |
| if ((getreg32(S32K3XX_SIRC_SR) & SIRC_SR_STATUS_ON) != 0) |
| { |
| return CGM_SIRC_FREQUENCY0; |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: s32k3xx_get_fircfreq |
| * |
| * Description: |
| * Gets Fast IRC clock frequency (FIRC). |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * The FIRC frequency. Zero is returned if the FIRC is invalid. |
| * |
| ****************************************************************************/ |
| |
| static uint32_t s32k3xx_get_fircfreq(void) |
| { |
| /* Check if the FIRC is valid */ |
| |
| if ((getreg32(S32K3XX_FIRC_STATUS) & FIRC_STATUS_ON) != 0) |
| { |
| /* TODO check HSE FIRC DIV register */ |
| |
| return CGM_FIRC_HIGHRANGE_FREQUENCY; |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: s32k3xx_get_pllfreq |
| * |
| * Description: |
| * Gets PLL clock frequency (SPLL). |
| * |
| * Input Parameters: |
| * uint32_t id |
| * |
| * Returned Value: |
| * The PLL frequency. Zero is returned if the PLL is invalid. |
| * |
| ****************************************************************************/ |
| |
| static uint32_t s32k3xx_get_pllfreq(void) |
| { |
| uint32_t freq; |
| uint32_t regval; |
| uint32_t prediv; |
| uint32_t postdiv; |
| uint32_t mult; |
| |
| /* Check if the PLL is valid */ |
| |
| if ((getreg32(S32K3XX_PLL_SR) & PLL_SR_LOCK) != 0) |
| { |
| /* Get System Oscillator frequency. */ |
| |
| freq = s32k3xx_get_fxoscfreq(); |
| if (freq != 0) |
| { |
| regval = getreg32(S32K3XX_PLL_DV); |
| prediv = ((regval & PLL_DV_RDIV_MASK) >> |
| PLL_DV_RDIV_SHIFT); |
| if (prediv == 0) |
| { |
| prediv = 1; |
| } |
| |
| mult = ((regval & PLL_DV_MFI_MASK) >> |
| PLL_DV_MFI_SHIFT); |
| postdiv = ((regval & PLL_DV_ODIV2_MASK) >> |
| PLL_DV_ODIV2_SHIFT); |
| if (postdiv == 0) |
| { |
| postdiv = 1; |
| } |
| |
| freq /= prediv; |
| freq *= mult; |
| freq /= postdiv; |
| } |
| |
| return freq; |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| |
| static uint32_t s32k3xx_get_phi0freq(void) |
| { |
| uint32_t regval; |
| uint32_t div; |
| |
| regval = getreg32(S32K3XX_PLL_ODIV0); |
| |
| div = ((regval & PLL_ODIV_DIV_MASK) >> PLL_ODIV_DIV_SHIFT); |
| |
| if (div == 0) |
| { |
| return 0; |
| } |
| else |
| { |
| return s32k3xx_get_pllfreq() / (div + 1); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: s32k3xx_get_scsfreq |
| * |
| * Description: |
| * Gets SCS current system clock frequence |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * 1 equals PHI1 0 equals FIRC |
| * |
| ****************************************************************************/ |
| |
| static inline uint32_t s32k3xx_get_scsfreq(void) |
| { |
| if (s32k3xx_get_scs_clk_source()) |
| { |
| return s32k3xx_get_phi0freq(); |
| } |
| else |
| { |
| return s32k3xx_get_fxoscfreq(); |
| } |
| } |
| |
| static uint32_t s32k3xx_mux_0_clocktransition(void) |
| { |
| /* Clock transition */ |
| |
| uint32_t timeout = 1000; |
| uint32_t regval; |
| |
| putreg32(1, S32K3XX_MC_CGM_MUX_0_DIV_TRIG); |
| |
| do |
| { |
| timeout--; |
| } |
| while ((getreg32(S32K3XX_MC_CGM_MUX_0_DIV_UPD_STAT) |
| & MC_CGM_MUX_DIV_UPD_STAT_DIV_STAT) != 0 && timeout > 0); |
| |
| if (timeout == 0) |
| { |
| return -ETIMEDOUT; |
| } |
| |
| timeout = 1000; |
| |
| do |
| { |
| timeout--; |
| } |
| while ((getreg32(S32K3XX_MC_CGM_MUX_0_CSS) |
| & MC_CGM_MUX_CSS_SWIP) != 0 && timeout > 0); |
| |
| if (timeout == 0) |
| { |
| return -ETIMEDOUT; |
| } |
| |
| regval = getreg32(S32K3XX_MC_CGM_MUX_0_CSC); |
| regval |= MC_CGM_MUX_CSC_CLK_SW; |
| putreg32(regval, S32K3XX_MC_CGM_MUX_0_CSC); |
| |
| timeout = 1000; |
| |
| do |
| { |
| timeout--; |
| } |
| while ((getreg32(S32K3XX_MC_CGM_MUX_0_CSS) |
| & MC_CGM_MUX_CSS_CLK_SW) == 0 && timeout > 0); |
| |
| if (timeout == 0) |
| { |
| return -ETIMEDOUT; |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| |
| static uint32_t s32k3xx_mux_x_clocktransition(uint32_t mux_csc_base) |
| { |
| /* Clock transition */ |
| |
| uint32_t timeout = 1000; |
| uint32_t regval; |
| |
| do |
| { |
| timeout--; |
| } |
| while ((getreg32(mux_csc_base + S32K3XX_MC_CGM_MUX_X_DIV_UPD_STAT_OFFSET) |
| & MC_CGM_MUX_DIV_UPD_STAT_DIV_STAT) != 0 && timeout > 0); |
| |
| if (timeout == 0) |
| { |
| return -ETIMEDOUT; |
| } |
| |
| timeout = 1000; |
| |
| do |
| { |
| timeout--; |
| } |
| while ((getreg32(mux_csc_base + S32K3XX_MC_CGM_MUX_X_CSS_OFFSET) |
| & MC_CGM_MUX_CSS_SWIP) != 0 && timeout > 0); |
| |
| if (timeout == 0) |
| { |
| return -ETIMEDOUT; |
| } |
| |
| regval = getreg32(mux_csc_base + S32K3XX_MC_CGM_MUX_X_CSC_OFFSET); |
| regval |= MC_CGM_MUX_CSC_CLK_SW; |
| putreg32(regval, mux_csc_base + S32K3XX_MC_CGM_MUX_X_CSC_OFFSET); |
| |
| timeout = 1000; |
| |
| do |
| { |
| timeout--; |
| } |
| while ((getreg32(mux_csc_base + S32K3XX_MC_CGM_MUX_X_CSS_OFFSET) |
| & MC_CGM_MUX_CSS_CLK_SW) == 0 && timeout > 0); |
| |
| if (timeout == 0) |
| { |
| return -ETIMEDOUT; |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| |
| static int s32k3xx_fxosc_peripheral(bool enable) |
| { |
| uint32_t regval; |
| uint32_t timeout; |
| |
| /* Check if clock is running */ |
| |
| regval = getreg32(S32K3XX_MC_ME_PRTN1_COFB1_STAT); |
| |
| if ((regval & MC_ME_PRTN1_COFB1_STAT_FXOSC) == 0) |
| { |
| /* Enable FXOSC pheripheral clock */ |
| |
| regval = getreg32(S32K3XX_MC_ME_PRTN1_COFB1_CLKEN); |
| |
| if (enable) |
| { |
| regval |= MC_ME_PRTN1_COFB1_CLKEN_FXOSC; |
| } |
| else |
| { |
| regval &= ~MC_ME_PRTN1_COFB1_CLKEN_FXOSC; |
| } |
| |
| putreg32(regval, S32K3XX_MC_ME_PRTN1_COFB1_CLKEN); |
| |
| /* Partition clock enable */ |
| |
| regval = MC_ME_PRTN_PCONF_PCE; |
| putreg32(regval, S32K3XX_MC_ME_PRTN1_PCONF); |
| |
| /* Update PRTN1 using Process update register */ |
| |
| regval = MC_ME_PRTN_PUPD_PCUD; |
| putreg32(regval, S32K3XX_MC_ME_PRTN1_PUPD); |
| |
| /* Control key register */ |
| |
| putreg32(MC_ME_CTL_KEY(0x5af0), S32K3XX_MC_ME_CTL_KEY); |
| putreg32(MC_ME_CTL_KEY(~0x5af0), S32K3XX_MC_ME_CTL_KEY); |
| |
| timeout = 1000; |
| |
| do |
| { |
| timeout--; |
| } |
| while ((getreg32(S32K3XX_MC_ME_PRTN1_COFB1_STAT) |
| & MC_ME_PRTN1_COFB1_STAT_FXOSC) == !enable); |
| |
| if (timeout == 0) |
| { |
| return -ETIMEDOUT; |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| |
| else |
| { |
| return 0; |
| } |
| } |
| |
| static int s32k3xx_mscm_peripheral(bool enable) |
| { |
| uint32_t regval; |
| uint32_t timeout; |
| |
| /* Check if clock is running */ |
| |
| regval = getreg32(S32K3XX_MC_ME_PRTN1_COFB0_STAT); |
| |
| if ((regval & MC_ME_PRTN1_COFB0_STAT_MSCM) == 0) |
| { |
| /* Enable MSCM pheripheral clock */ |
| |
| regval = getreg32(S32K3XX_MC_ME_PRTN1_COFB0_CLKEN); |
| if (enable) |
| { |
| regval |= MC_ME_PRTN1_COFB0_CLKEN_MSCM; |
| } |
| else |
| { |
| regval &= ~MC_ME_PRTN1_COFB0_CLKEN_MSCM; |
| } |
| |
| putreg32(regval, S32K3XX_MC_ME_PRTN1_COFB0_CLKEN); |
| |
| /* Partition clock enable */ |
| |
| putreg32(MC_ME_PRTN_PCONF_PCE, S32K3XX_MC_ME_PRTN1_PCONF); |
| |
| /* Update PRTN1 using Process update register */ |
| |
| putreg32(MC_ME_PRTN_PUPD_PCUD, S32K3XX_MC_ME_PRTN1_PUPD); |
| |
| /* Control key register */ |
| |
| putreg32(MC_ME_CTL_KEY(0x5af0), S32K3XX_MC_ME_CTL_KEY); |
| putreg32(MC_ME_CTL_KEY(~0x5af0), S32K3XX_MC_ME_CTL_KEY); |
| |
| timeout = 1000; |
| |
| do |
| { |
| timeout--; |
| } |
| while ((getreg32(S32K3XX_MC_ME_PRTN1_COFB0_STAT) |
| & MC_ME_PRTN1_COFB0_STAT_MSCM) == !enable && timeout > 0); |
| |
| if (timeout == 0) |
| { |
| return -ETIMEDOUT; |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int s32k3xx_pll_peripheral(bool enable) |
| { |
| uint32_t regval; |
| uint32_t timeout; |
| |
| /* Check if clock is running */ |
| |
| regval = getreg32(S32K3XX_MC_ME_PRTN1_COFB1_STAT); |
| |
| if ((regval & MC_ME_PRTN1_COFB1_STAT_PLL) == 0) |
| { |
| /* Enable FXOSC pheripheral clock */ |
| |
| regval = getreg32(S32K3XX_MC_ME_PRTN1_COFB1_CLKEN); |
| if (enable) |
| { |
| regval |= MC_ME_PRTN1_COFB1_CLKEN_PLL; |
| } |
| else |
| { |
| regval &= ~MC_ME_PRTN1_COFB1_CLKEN_PLL; |
| } |
| |
| putreg32(regval, S32K3XX_MC_ME_PRTN1_COFB1_CLKEN); |
| |
| /* Partition clock enable */ |
| |
| putreg32(MC_ME_PRTN_PCONF_PCE, S32K3XX_MC_ME_PRTN1_PCONF); |
| |
| /* Update PRTN1 using Process update register */ |
| |
| putreg32(MC_ME_PRTN_PUPD_PCUD, S32K3XX_MC_ME_PRTN1_PUPD); |
| |
| /* Control key register */ |
| |
| putreg32(MC_ME_CTL_KEY(0x5af0), S32K3XX_MC_ME_CTL_KEY); |
| putreg32(MC_ME_CTL_KEY(~0x5af0), S32K3XX_MC_ME_CTL_KEY); |
| |
| timeout = 1000; |
| |
| do |
| { |
| timeout--; |
| } |
| while ((getreg32(S32K3XX_MC_ME_PRTN1_COFB1_STAT) |
| & MC_ME_PRTN1_COFB1_STAT_PLL) == !enable && timeout > 0); |
| |
| if (timeout == 0) |
| { |
| return -ETIMEDOUT; |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: s32k3xx_pll_config |
| * |
| * Description: |
| * Configure PLL clocking. |
| * |
| * Input Parameters: |
| * pllcfg - Describes the new PLL clock configuration |
| * |
| * Returned Value: |
| * Zero (OK) is returned a success; A negated errno value is returned on |
| * any failure. |
| * |
| ****************************************************************************/ |
| |
| static int s32k3xx_pll_config(const struct cgm_pll_config_s *pllcfg) |
| { |
| uint32_t regval; |
| int32_t retval; |
| uint32_t timeout; |
| |
| /* Enable FXOSC peripheral */ |
| |
| retval = s32k3xx_fxosc_peripheral(true); |
| |
| if (retval < 0) |
| { |
| return retval; |
| } |
| |
| /* Enable FXOSC for XTAL */ |
| |
| regval = getreg32(S32K3XX_FXOSC_CTRL); |
| |
| regval &= ~(FXOSC_CTRL_OSC_BYP); |
| |
| /* FIXME make EOCV and GM_SEL configurable */ |
| |
| regval |= (FXOSC_CTRL_COMP_EN | FXOSC_CTRL_EOCV(157) | |
| FXOSC_CTRL_GM_SEL_0_7016X | FXOSC_CTRL_OSCON); |
| |
| putreg32(regval, S32K3XX_FXOSC_CTRL); |
| |
| /* Enable PLL peripheral */ |
| |
| retval = s32k3xx_pll_peripheral(true); |
| |
| if (retval < 0) |
| { |
| return retval; |
| } |
| |
| /* Setup PLL Divider */ |
| |
| regval = (PLL_DV_RDIV_DIV(pllcfg->prediv) | PLL_DV_MFI(pllcfg->mult) | |
| PLL_DV_ODIV2_DIV(pllcfg->postdiv)); |
| |
| putreg32(regval, S32K3XX_PLL_DV); |
| |
| /* Setup PLL Fractional Divider */ |
| |
| regval = getreg32(S32K3XX_PLL_FD); |
| |
| regval &= ~(PLL_FD_SDMEN | PLL_FD_SDM2); |
| |
| putreg32(regval, S32K3XX_PLL_FD); |
| |
| /* Setup PLL Frequency Modulation */ |
| |
| regval = getreg32(S32K3XX_PLL_FM); |
| |
| regval &= ~(PLL_FM_STEPSIZE_MASK | PLL_FM_SPREADCTL | |
| PLL_FM_STEPNO_MASK); |
| |
| regval |= PLL_FM_SSCGBYP | PLL_FM_SPREADCTL; |
| |
| putreg32(regval, S32K3XX_PLL_FM); |
| |
| /* Set PLL_PHI0 divider */ |
| |
| if (pllcfg->phi0 == CGM_PLL_PHI_DIV_DISABLE) |
| { |
| putreg32(0, S32K3XX_PLL_ODIV0); |
| } |
| else |
| { |
| putreg32((PLL_ODIV_DIV(pllcfg->phi0) |
| | PLL_ODIV_DE), S32K3XX_PLL_ODIV0); |
| } |
| |
| /* Set PLL_PHI1 divider */ |
| |
| if (pllcfg->phi1 == CGM_PLL_PHI_DIV_DISABLE) |
| { |
| putreg32(0, S32K3XX_PLL_ODIV1); |
| } |
| else |
| { |
| putreg32((PLL_ODIV_DIV(pllcfg->phi1) |
| | PLL_ODIV_DE), S32K3XX_PLL_ODIV1); |
| } |
| |
| /* enable PLL */ |
| |
| putreg32(0, S32K3XX_PLL_CR); |
| |
| timeout = 1000; |
| |
| do |
| { |
| timeout--; |
| } |
| while ((getreg32(S32K3XX_PLL_SR) |
| & PLL_SR_LOCK) == 0); |
| |
| if (timeout == 0) |
| { |
| return -ETIMEDOUT; |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: s32k3xx_scs_config |
| * |
| * Description: |
| * Configure SCS clocking. |
| * |
| * Input Parameters: |
| * scscfg - Describes the new SCS clock configuration |
| * |
| * Returned Value: |
| * Zero (OK) is returned a success; A negated errno value is returned on |
| * any failure. |
| * |
| ****************************************************************************/ |
| |
| static int s32k3xx_scs_config(const struct cgm_scs_config_s *scscfg) |
| { |
| if (scscfg->scs_source == CGM_SCS_SOURCE_FIRC) |
| { |
| putreg32(MC_CGM_MUX_CSC_SELCTL_FIRC |
| | MC_CGM_MUX_CSC_CLK_SW, |
| S32K3XX_MC_CGM_MUX_0_CSC); |
| } |
| else if (scscfg->scs_source == CGM_SCS_SOURCE_PLL_PHI0) |
| { |
| putreg32(MC_CGM_MUX_CSC_SELCTL_PLL_PHI0_CLK |
| | MC_CGM_MUX_CSC_CLK_SW, |
| S32K3XX_MC_CGM_MUX_0_CSC); |
| } |
| |
| if (scscfg->core_clk.div != CGM_MUX_DISABLE) |
| { |
| putreg32((MC_CGM_MUX_DC_DE |
| | MC_CGM_MUX_DC_DIV(scscfg->core_clk.div)), |
| S32K3XX_MC_CGM_MUX_0_DC_0); |
| } |
| else |
| { |
| putreg32(0, S32K3XX_MC_CGM_MUX_0_DC_0); |
| } |
| |
| if (scscfg->aips_plat_clk.div != CGM_MUX_DISABLE) |
| { |
| putreg32((MC_CGM_MUX_DC_DE |
| | MC_CGM_MUX_DC_DIV(scscfg->aips_plat_clk.div)), |
| S32K3XX_MC_CGM_MUX_0_DC_1); |
| } |
| else |
| { |
| putreg32(0, S32K3XX_MC_CGM_MUX_0_DC_1); |
| } |
| |
| if (scscfg->aips_slow_clk.div != CGM_MUX_SLOW_DISABLE) |
| { |
| putreg32((MC_CGM_MUX_DC_DE |
| | MC_CGM_MUX_DC_DIV(scscfg->aips_slow_clk.div)), |
| S32K3XX_MC_CGM_MUX_0_DC_2); |
| } |
| else |
| { |
| putreg32(0, S32K3XX_MC_CGM_MUX_0_DC_2); |
| } |
| |
| if (scscfg->hse_clk.div != CGM_MUX_DISABLE) |
| { |
| putreg32((MC_CGM_MUX_DC_DE |
| | MC_CGM_MUX_DC_DIV(scscfg->hse_clk.div)), |
| S32K3XX_MC_CGM_MUX_0_DC_3); |
| } |
| else |
| { |
| putreg32(0, S32K3XX_MC_CGM_MUX_0_DC_3); |
| } |
| |
| if (scscfg->dcm_clk.div != CGM_MUX_DISABLE) |
| { |
| putreg32((MC_CGM_MUX_DC_DE |
| | MC_CGM_MUX_DC_DIV(scscfg->dcm_clk.div)), |
| S32K3XX_MC_CGM_MUX_0_DC_4); |
| } |
| else |
| { |
| putreg32(0, S32K3XX_MC_CGM_MUX_0_DC_4); |
| } |
| |
| if (scscfg->lbist_clk.div != CGM_MUX_DISABLE) |
| { |
| putreg32((MC_CGM_MUX_DC_DE |
| | MC_CGM_MUX_DC_DIV(scscfg->lbist_clk.div)), |
| S32K3XX_MC_CGM_MUX_0_DC_5); |
| } |
| else |
| { |
| putreg32(0, S32K3XX_MC_CGM_MUX_0_DC_5); |
| } |
| |
| #ifdef CONFIG_S32K3XX_QSPI |
| if (scscfg->qspi_mem_clk.div != CGM_MUX_DISABLE) |
| { |
| putreg32((MC_CGM_MUX_DC_DE |
| | MC_CGM_MUX_DC_DIV(scscfg->qspi_mem_clk.div)), |
| S32K3XX_MC_CGM_MUX_0_DC_6); |
| } |
| else |
| { |
| putreg32(0, S32K3XX_MC_CGM_MUX_0_DC_6); |
| } |
| #endif |
| |
| if (scscfg->mux_1_stm0.div == CGM_MUX_DISABLE) |
| { |
| putreg32(0, S32K3XX_MC_CGM_MUX_1_DC_0); |
| } |
| else |
| { |
| putreg32((scscfg->mux_1_stm0.source |
| << MC_CGM_MUX_CSS_SELSTAT_SHIFT), |
| S32K3XX_MC_CGM_MUX_1_CSC); |
| putreg32((MC_CGM_MUX_DC_DIV(scscfg->mux_1_stm0.div) |
| | MC_CGM_MUX_DC_DE), |
| S32K3XX_MC_CGM_MUX_1_DC_0); |
| } |
| |
| if (scscfg->mux_3.div == CGM_MUX_DISABLE) |
| { |
| putreg32(0, S32K3XX_MC_CGM_MUX_3_DC_0); |
| } |
| else |
| { |
| putreg32((scscfg->mux_3.source |
| << MC_CGM_MUX_CSS_SELSTAT_SHIFT), |
| S32K3XX_MC_CGM_MUX_3_CSC); |
| putreg32((MC_CGM_MUX_DC_DIV(scscfg->mux_3.div) |
| | MC_CGM_MUX_DC_DE), |
| S32K3XX_MC_CGM_MUX_3_DC_0); |
| } |
| |
| if (scscfg->mux_4.div == CGM_MUX_DISABLE) |
| { |
| putreg32(0, S32K3XX_MC_CGM_MUX_4_DC_0); |
| } |
| else |
| { |
| putreg32((scscfg->mux_4.source |
| << MC_CGM_MUX_CSS_SELSTAT_SHIFT), |
| S32K3XX_MC_CGM_MUX_4_CSC); |
| putreg32((MC_CGM_MUX_DC_DIV(scscfg->mux_4.div) |
| | MC_CGM_MUX_DC_DE), |
| S32K3XX_MC_CGM_MUX_4_DC_0); |
| } |
| |
| #ifdef CONFIG_S32K3XX_ENET |
| if (scscfg->mux_7_emac_rx.div == CGM_MUX_DISABLE) |
| { |
| putreg32(0, S32K3XX_MC_CGM_MUX_7_DC_0); |
| } |
| else |
| { |
| putreg32((scscfg->mux_7_emac_rx.source |
| << MC_CGM_MUX_CSS_SELSTAT_SHIFT), |
| S32K3XX_MC_CGM_MUX_7_CSC); |
| putreg32((MC_CGM_MUX_DC_DIV(scscfg->mux_7_emac_rx.div) |
| | MC_CGM_MUX_DC_DE), |
| S32K3XX_MC_CGM_MUX_7_DC_0); |
| } |
| |
| if (scscfg->mux_8_emac_tx.div == CGM_MUX_DISABLE) |
| { |
| putreg32(0, S32K3XX_MC_CGM_MUX_8_DC_0); |
| } |
| else |
| { |
| putreg32((scscfg->mux_8_emac_tx.source |
| << MC_CGM_MUX_CSS_SELSTAT_SHIFT), |
| S32K3XX_MC_CGM_MUX_8_CSC); |
| putreg32((MC_CGM_MUX_DC_DIV(scscfg->mux_8_emac_tx.div) |
| | MC_CGM_MUX_DC_DE), |
| S32K3XX_MC_CGM_MUX_8_DC_0); |
| } |
| |
| if (scscfg->mux_9_emac_ts.div == CGM_MUX_DISABLE) |
| { |
| putreg32(0, S32K3XX_MC_CGM_MUX_9_DC_0); |
| } |
| else |
| { |
| putreg32((scscfg->mux_9_emac_ts.source |
| << MC_CGM_MUX_CSS_SELSTAT_SHIFT), |
| S32K3XX_MC_CGM_MUX_9_CSC); |
| putreg32((MC_CGM_MUX_DC_DIV(scscfg->mux_9_emac_ts.div) |
| | MC_CGM_MUX_DC_DE), |
| S32K3XX_MC_CGM_MUX_9_DC_0); |
| } |
| #endif |
| |
| #ifdef CONFIG_S32K3XX_QSPI |
| if (scscfg->mux_10_qspi_sfck.div == CGM_MUX_DISABLE) |
| { |
| putreg32(0, S32K3XX_MC_CGM_MUX_10_DC_0); |
| } |
| else |
| { |
| putreg32((scscfg->mux_10_qspi_sfck.source |
| << MC_CGM_MUX_CSS_SELSTAT_SHIFT), |
| S32K3XX_MC_CGM_MUX_10_CSC); |
| putreg32((MC_CGM_MUX_DC_DIV(scscfg->mux_10_qspi_sfck.div) |
| | MC_CGM_MUX_DC_DE), |
| S32K3XX_MC_CGM_MUX_10_DC_0); |
| } |
| #endif |
| |
| return 0; |
| } |
| |
| static void s32k3xx_clkout_config(const struct |
| cgm_clkout_config_s *clkoutcfg) |
| { |
| if (clkoutcfg->div == CGM_CLKOUT_DIV_DISABLE) |
| { |
| putreg32(0, S32K3XX_MC_CGM_MUX_6_DC_0); |
| } |
| else |
| { |
| putreg32((clkoutcfg->source << MC_CGM_MUX_CSS_SELSTAT_SHIFT), |
| S32K3XX_MC_CGM_MUX_6_CSC); |
| putreg32((MC_CGM_MUX_DC_DIV(clkoutcfg->div) | MC_CGM_MUX_DC_DE), |
| S32K3XX_MC_CGM_MUX_6_DC_0); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: s32k1xx_scg_config |
| * |
| * Description: |
| * Configure CGM clocking. |
| * |
| * Input Parameters: |
| * cgmcfg - Describes the new CGM clock configuration |
| * |
| * Returned Value: |
| * Zero (OK) is returned a success; A negated errno value is returned on |
| * any failure. |
| * |
| ****************************************************************************/ |
| |
| static int s32k3xx_cgm_config(const struct cgm_config_s *cgmcfg) |
| { |
| int ret; |
| |
| /* Enable MSCM peripheral */ |
| |
| s32k3xx_mscm_peripheral(true); |
| |
| /* Enable PLL peripheral */ |
| |
| s32k3xx_pll_peripheral(true); |
| |
| /* Configure MC_CGM_MUX0 to run from FIRC clock to allow PLL reconfig. */ |
| |
| putreg32((MC_CGM_MUX_0_DIV_TRIG_CTRL_TCTL | |
| MC_CGM_MUX_0_DIV_TRIG_CTRL_HHEN), |
| S32K3XX_MC_CGM_MUX_0_DIV_TRIG_CTRL); |
| putreg32((MC_CGM_MUX_DC_DIV(0) | MC_CGM_MUX_DC_DE), |
| S32K3XX_MC_CGM_MUX_0_DC_0); |
| putreg32((MC_CGM_MUX_DC_DIV(0) | MC_CGM_MUX_DC_DE), |
| S32K3XX_MC_CGM_MUX_0_DC_1); |
| putreg32((MC_CGM_MUX_DC_DIV(2) | MC_CGM_MUX_DC_DE), |
| S32K3XX_MC_CGM_MUX_0_DC_2); |
| putreg32((MC_CGM_MUX_DC_DIV(0) | MC_CGM_MUX_DC_DE), |
| S32K3XX_MC_CGM_MUX_0_DC_3); |
| putreg32((MC_CGM_MUX_DC_DIV(0) | MC_CGM_MUX_DC_DE), |
| S32K3XX_MC_CGM_MUX_0_DC_4); |
| putreg32((MC_CGM_MUX_DC_DIV(0) | MC_CGM_MUX_DC_DE), |
| S32K3XX_MC_CGM_MUX_0_DC_5); |
| putreg32((MC_CGM_MUX_DC_DIV(0) | MC_CGM_MUX_DC_DE), |
| S32K3XX_MC_CGM_MUX_0_DC_6); |
| |
| /* Transition to FIRC */ |
| |
| ret = s32k3xx_mux_0_clocktransition(); |
| |
| if (ret >= 0) |
| { |
| ret = s32k3xx_pll_config(&cgmcfg->pll); |
| } |
| |
| if (ret >= 0) |
| { |
| ret = s32k3xx_scs_config(&cgmcfg->scs); |
| if (ret >= 0) |
| { |
| /* Transition to PLL */ |
| |
| ret = s32k3xx_mux_0_clocktransition(); |
| } |
| } |
| |
| if (ret >= 0) |
| { |
| ret = s32k3xx_mux_x_clocktransition(S32K3XX_MC_CGM_MUX_1_CSC); |
| } |
| |
| if (ret >= 0) |
| { |
| ret = s32k3xx_mux_x_clocktransition(S32K3XX_MC_CGM_MUX_3_CSC); |
| } |
| |
| if (ret >= 0) |
| { |
| ret = s32k3xx_mux_x_clocktransition(S32K3XX_MC_CGM_MUX_4_CSC); |
| } |
| |
| #ifdef CONFIG_S32K3XX_ENET |
| if (ret >= 0) |
| { |
| # ifdef PIN_EMAC_MII_RMII_TX_CLK |
| s32k3xx_pinconfig(PIN_EMAC_MII_RMII_TX_CLK); |
| # endif |
| ret = s32k3xx_mux_x_clocktransition(S32K3XX_MC_CGM_MUX_7_CSC); |
| ret = s32k3xx_mux_x_clocktransition(S32K3XX_MC_CGM_MUX_8_CSC); |
| ret = s32k3xx_mux_x_clocktransition(S32K3XX_MC_CGM_MUX_9_CSC); |
| } |
| #endif |
| |
| #ifdef CONFIG_S32K3XX_QSPI |
| if (ret >= 0) |
| { |
| s32k3xx_mux_x_clocktransition(S32K3XX_MC_CGM_MUX_10_CSC); |
| } |
| #endif |
| |
| s32k3xx_clkout_config(&cgmcfg->clkout); |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: s32k3xx_clockconfig |
| * |
| * Description: |
| * Called to initialize the S32K3XX. 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 s32k3xx_clockconfig(const struct clock_configuration_s *clkcfg) |
| { |
| int ret; |
| |
| ret = s32k3xx_cgm_config(&clkcfg->cgm); |
| if (ret >= 0) |
| { |
| s32k3xx_periphclocks(num_of_peripheral_clocks_0, clkcfg->pcc.pclks); |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: s32k3xx_get_freq |
| * |
| * Description: |
| * clock frequency from a clock source. |
| * |
| * Input Parameters: |
| * clksrc - The requested clock source. |
| * |
| * Returned Value: |
| * The frequency of the requested clock source. |
| * |
| ****************************************************************************/ |
| |
| uint32_t s32k3xx_get_freq(enum clock_names_e clksrc) |
| { |
| uint32_t regval; |
| |
| switch (clksrc) |
| { |
| case CORE_CLK: |
| { |
| regval = getreg32(S32K3XX_MC_CGM_MUX_0_DC_0); |
| if ((regval & MC_CGM_MUX_DC_DE) == 0) |
| { |
| return 0; |
| } |
| else |
| { |
| regval = ((regval & MC_CGM_MUX_DC_DIV_MASK) |
| >> MC_CGM_MUX_DC_DIV_SHIFT); |
| return s32k3xx_get_scsfreq() / (regval + 1); |
| } |
| } |
| break; |
| |
| case SIRC_CLK: |
| return s32k3xx_get_sircfreq(); |
| |
| case FIRC_CLK: |
| return s32k3xx_get_fircfreq(); |
| |
| case AIPS_PLAT_CLK: |
| { |
| regval = getreg32(S32K3XX_MC_CGM_MUX_0_DC_1); |
| if ((regval & MC_CGM_MUX_DC_DE) == 0) |
| { |
| return 0; |
| } |
| else |
| { |
| regval = ((regval & MC_CGM_MUX_DC_DIV_MASK) |
| >> MC_CGM_MUX_DC_DIV_SHIFT); |
| return s32k3xx_get_scsfreq() / (regval + 1); |
| } |
| } |
| break; |
| |
| case AIPS_SLOW_CLK: |
| { |
| regval = getreg32(S32K3XX_MC_CGM_MUX_0_DC_2); |
| if ((regval & MC_CGM_MUX_DC_DE) == 0) |
| { |
| return 0; |
| } |
| else |
| { |
| regval = ((regval & MC_CGM_MUX_DC_DIV_MASK) |
| >> MC_CGM_MUX_DC_DIV_SHIFT); |
| return s32k3xx_get_scsfreq() / (regval + 1); |
| } |
| } |
| break; |
| |
| case STM0_CLK: |
| { |
| regval = getreg32(S32K3XX_MC_CGM_MUX_1_DC_0); |
| if ((regval & MC_CGM_MUX_DC_DE) == 0) |
| { |
| return 0; |
| } |
| else |
| { |
| switch (getreg32(S32K3XX_MC_CGM_MUX_1_CSC)) |
| { |
| case (CGM_CLK_SRC_AIPS_PLAT_CLK |
| << MC_CGM_MUX_CSS_SELSTAT_SHIFT): |
| regval = ((regval & MC_CGM_MUX_DC_DIV_MASK) |
| >> MC_CGM_MUX_DC_DIV_SHIFT); |
| return s32k3xx_get_freq(AIPS_PLAT_CLK) / (regval + 1); |
| |
| case (CGM_CLK_SRC_FXOSC << MC_CGM_MUX_CSS_SELSTAT_SHIFT): |
| regval = ((regval & MC_CGM_MUX_DC_DIV_MASK) |
| >> MC_CGM_MUX_DC_DIV_SHIFT); |
| return s32k3xx_get_freq(FXOSC_CLK) / (regval + 1); |
| |
| case (CGM_CLK_SRC_FIRC << MC_CGM_MUX_CSS_SELSTAT_SHIFT): |
| regval = ((regval & MC_CGM_MUX_DC_DIV_MASK) |
| >> MC_CGM_MUX_DC_DIV_SHIFT); |
| return s32k3xx_get_freq(FIRC_CLK) / (regval + 1); |
| |
| default: |
| return 0; |
| } |
| } |
| } |
| break; |
| |
| case FLEXCAN0_CLK: |
| case FLEXCAN1_CLK: |
| case FLEXCAN2_CLK: |
| { |
| regval = getreg32(S32K3XX_MC_CGM_MUX_3_DC_0); |
| if ((regval & MC_CGM_MUX_DC_DE) == 0) |
| { |
| return 0; |
| } |
| else |
| { |
| regval = ((regval & MC_CGM_MUX_DC_DIV_MASK) |
| >> MC_CGM_MUX_DC_DIV_SHIFT); |
| return s32k3xx_get_freq(AIPS_PLAT_CLK) / (regval + 1); |
| } |
| } |
| break; |
| |
| case FLEXCAN3_CLK: |
| case FLEXCAN4_CLK: |
| case FLEXCAN5_CLK: |
| { |
| regval = getreg32(S32K3XX_MC_CGM_MUX_4_DC_0); |
| if ((regval & MC_CGM_MUX_DC_DE) == 0) |
| { |
| return 0; |
| } |
| else |
| { |
| regval = ((regval & MC_CGM_MUX_DC_DIV_MASK) |
| >> MC_CGM_MUX_DC_DIV_SHIFT); |
| return s32k3xx_get_freq(AIPS_PLAT_CLK) / (regval + 1); |
| } |
| } |
| break; |
| |
| default: |
| { |
| /* Invalid clock source type */ |
| |
| DEBUGPANIC(); |
| return 0; |
| } |
| break; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: s32k3xx_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 s32k3xx_get_coreclk(void) |
| { |
| return s32k3xx_get_freq(CORE_CLK); |
| } |