diff --git a/arch/arm/src/s32k1xx/Make.defs b/arch/arm/src/s32k1xx/Make.defs index 9b343813f91..fb252ea64bf 100644 --- a/arch/arm/src/s32k1xx/Make.defs +++ b/arch/arm/src/s32k1xx/Make.defs @@ -93,7 +93,11 @@ ifeq ($(CONFIG_S32K1XX_EEEPROM),y) CHIP_CSRCS += s32k1xx_eeeprom.c endif -ifeq ($(CONFIG_RESET_CAUSE_PROC_FS), y) +ifneq ($(CONFIG_ARCH_CUSTOM_PMINIT),y) +CHIP_CSRCS += s32k1xx_pminitialize.c +endif + +ifeq ($(CONFIG_RESET_CAUSE_PROC_FS), y) CHIP_CSRCS += s32k1xx_resetcause.c endif diff --git a/arch/arm/src/s32k1xx/hardware/s32k1xx_pmc.h b/arch/arm/src/s32k1xx/hardware/s32k1xx_pmc.h index d3e20430c85..4c17c1adf2c 100644 --- a/arch/arm/src/s32k1xx/hardware/s32k1xx_pmc.h +++ b/arch/arm/src/s32k1xx/hardware/s32k1xx_pmc.h @@ -63,6 +63,7 @@ /* Regulator Status and Control Register */ +#define PMC_REGSC_BIASEN (1 << 0) /* Bit 0: Bias Enable Bit */ #define PMC_REGSC_CLKBIASDIS (1 << 1) /* Bit 1: Clock Bias Disable Bit */ #define PMC_REGSC_REGFPM (1 << 2) /* Bit 2: Regulator in Full Performance Mode Status Bit */ #define PMC_REGSC_LPOSTAT (1 << 6) /* Bit 6: LPO Status Bit */ diff --git a/arch/arm/src/s32k1xx/hardware/s32k1xx_smc.h b/arch/arm/src/s32k1xx/hardware/s32k1xx_smc.h index 5da2137a15c..0d3c7b21ecf 100644 --- a/arch/arm/src/s32k1xx/hardware/s32k1xx_smc.h +++ b/arch/arm/src/s32k1xx/hardware/s32k1xx_smc.h @@ -54,13 +54,12 @@ /* SMC Version ID Register */ -#define SMC_VERID_FEATURE_SHIFT (0) /* Bits 0-15: Feature Identification Number */ +#define SMC_VERID_FEATURE_SHIFT (0) /* Bits 0-15: Feature Identification Number */ #define SMC_VERID_FEATURE_MASK (0xffff << SMC_VERID_FEATURE_SHIFT) # define SMC_VERID_FEATURE_STD (1 << SMC_VERID_FEATURE_SHIFT) /* Standard feature set */ - -#define SMC_VERID_MINOR_SHIFT (16) /* Bits 16-23: Minor Version Number */ +#define SMC_VERID_MINOR_SHIFT (16) /* Bits 16-23: Minor Version Number */ #define SMC_VERID_MINOR_MASK (0xff << SMC_VERID_MINOR_SHIFT) -#define SMC_VERID_MAJOR_SHIFT (24) /* Bits 24-31: Major Version Number */ +#define SMC_VERID_MAJOR_SHIFT (24) /* Bits 24-31: Major Version Number */ #define SMC_VERID_MAJOR_MASK (0xff << SMC_VERID_MAJOR_SHIFT) /* SMC Parameter Register */ @@ -72,8 +71,10 @@ /* SMC Power Mode Protection register */ -#define SMC_PMPROT_AVLP (1 << 5) /* Bit 5: Allow Very-Low-Power Modes */ -#define SMC_PMPROT_AHSRUN (1 << 7) /* Bit 7: Allow High Speed Run mode */ +#define SMC_PMPROT_AVLP_SHIFT (5) /* Bit 5: Allow Very-Low-Power Modes */ +#define SMC_PMPROT_AVLP (1 << SMC_PMPROT_AVLP_SHIFT) +#define SMC_PMPROT_AHSRUN_SHIFT (7) /* Bit 7: Allow High Speed Run mode */ +#define SMC_PMPROT_AHSRUN (1 << SMC_PMPROT_AHSRUN_SHIFT) /* SMC Power Mode Control register */ diff --git a/arch/arm/src/s32k1xx/s32k1xx_clockconfig.c b/arch/arm/src/s32k1xx/s32k1xx_clockconfig.c index 04e806ee25c..880ee19edbc 100644 --- a/arch/arm/src/s32k1xx/s32k1xx_clockconfig.c +++ b/arch/arm/src/s32k1xx/s32k1xx_clockconfig.c @@ -63,6 +63,7 @@ #include #include +#include #include "arm_arch.h" #include "arm_internal.h" @@ -73,6 +74,7 @@ #include "hardware/s32k1xx_pmc.h" #include "s32k1xx_periphclocks.h" #include "s32k1xx_clockconfig.h" +#include "s32k1xx_start.h" #include /* Include last. May have dependencies */ @@ -120,19 +122,33 @@ #define SCG_SPLL_REF_MIN 8000000 #define SCG_SPLL_REF_MAX 32000000 +/* Power management definitions */ + +#if defined(CONFIG_PM) +#ifndef PM_IDLE_DOMAIN +# define PM_IDLE_DOMAIN 0 /* Revisit */ +#endif +#endif + +#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 ****************************************************************************/ -enum scg_system_clock_mode_e -{ - SCG_SYSTEM_CLOCK_MODE_CURRENT = 0, /* Current mode. */ - SCG_SYSTEM_CLOCK_MODE_RUN = 1, /* Run mode. */ - SCG_SYSTEM_CLOCK_MODE_VLPR = 2, /* Very Low Power Run mode. */ - SCG_SYSTEM_CLOCK_MODE_HSRUN = 3, /* High Speed Run mode. */ - SCG_SYSTEM_CLOCK_MODE_NONE /* MAX value. */ -}; - /**************************************************************************** * Private Data ****************************************************************************/ @@ -212,6 +228,14 @@ static uint32_t g_rtc_clkin; /* RTC CLKIN clock */ 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 ****************************************************************************/ @@ -236,59 +260,6 @@ static inline uint32_t s32k1xx_get_scgclk_source(void) SCG_CSR_SCS_SHIFT); } -/**************************************************************************** - * Name: s32k1xx_get_runmode - * - * Description: - * Get the current running mode. - * - * Input Parameters: - * None - * - * Returned Value: - * The current running mode. - * - ****************************************************************************/ - -static 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_get_soscfreq * @@ -783,7 +754,7 @@ static int s32k1xx_firc_config(bool enable, } /**************************************************************************** - * Name: s32k11_firc_clocksource + * Name: s32k1xx_firc_clocksource * * Description: * Configure to the FIRC clock source. @@ -797,7 +768,7 @@ static int s32k1xx_firc_config(bool enable, * ****************************************************************************/ -static int s32k11_firc_clocksource(void) +static int s32k1xx_firc_clocksource(void) { struct scg_system_clock_config_s firccfg; int ret = OK; @@ -944,6 +915,61 @@ static int s32k1xx_sirc_config(bool enable, 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 * @@ -1394,9 +1420,10 @@ static int s32k1xx_scg_config(const struct scg_config_s *scgcfg) DEBUGASSERT(scgcfg != NULL); - /* Configure a temporary system clock source: FIRC */ + /* Configure a temporary system clock source: FIRC if enabled */ + + ret = s32k1xx_firc_clocksource(); - ret = s32k11_firc_clocksource(); if (ret == OK) { /* Configure clock sources from SCG */ @@ -1654,6 +1681,10 @@ static void s32k1xx_pmc_config(const struct pmc_config_s *pmccfg) 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. */ @@ -1662,10 +1693,843 @@ static void s32k1xx_pmc_config(const struct pmc_config_s *pmccfg) } } +/**************************************************************************** + * 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 */ + + DEBUGASSERT(false); + } + } + + /* if it is 0 */ + + else + { + /* error */ + + DEBUGASSERT(false); + } + + /* 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 */ + + DEBUGASSERT(false); + } + } + + /* if it is 0 */ + + else + { + /* error */ + + DEBUGASSERT(false); + } + +#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 */ + + DEBUGASSERT(false); + } + } + + /* if it is 0 */ + + else + { + /* error */ + + DEBUGASSERT(false); + } + +#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 * @@ -1690,11 +2554,22 @@ int s32k1xx_clockconfig(const struct clock_configuration_s *clkcfg) DEBUGASSERT(clkcfg != NULL); +#ifdef CONFIG_PM + /* Register to receive power management callbacks */ + + ret = pm_register(&g_clock_pmcb); + DEBUGASSERT(ret == OK); +#endif + /* 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); diff --git a/arch/arm/src/s32k1xx/s32k1xx_clockconfig.h b/arch/arm/src/s32k1xx/s32k1xx_clockconfig.h index 05fdf2e8bd9..9ed9d8e338d 100644 --- a/arch/arm/src/s32k1xx/s32k1xx_clockconfig.h +++ b/arch/arm/src/s32k1xx/s32k1xx_clockconfig.h @@ -440,6 +440,15 @@ struct clock_configuration_s struct pmc_config_s pmc; /* PMC Clock configuration */ }; +enum scg_system_clock_mode_e +{ + SCG_SYSTEM_CLOCK_MODE_CURRENT = 0, /* Current mode. */ + SCG_SYSTEM_CLOCK_MODE_RUN = 1, /* Run mode. */ + SCG_SYSTEM_CLOCK_MODE_VLPR = 2, /* Very Low Power Run mode. */ + SCG_SYSTEM_CLOCK_MODE_HSRUN = 3, /* High Speed Run mode. */ + SCG_SYSTEM_CLOCK_MODE_NONE /* MAX value. */ +}; + /**************************************************************************** * Inline Functions ****************************************************************************/ @@ -463,6 +472,39 @@ extern "C" * Public Function Prototypes ****************************************************************************/ +/**************************************************************************** + * 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); + +/**************************************************************************** + * 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); + /**************************************************************************** * Name: s32k1xx_clockconfig * diff --git a/arch/arm/src/s32k1xx/s32k1xx_lpspi.c b/arch/arm/src/s32k1xx/s32k1xx_lpspi.c index 2f9000461da..60ea04d78e7 100644 --- a/arch/arm/src/s32k1xx/s32k1xx_lpspi.c +++ b/arch/arm/src/s32k1xx/s32k1xx_lpspi.c @@ -100,6 +100,25 @@ # error "Cannot enable both interrupt mode and DMA mode for SPI" #endif +/* Power management definitions */ + +#if defined(CONFIG_PM) && !defined(CONFIG_S32K1XX_PM_SPI_ACTIVITY) +# define CONFIG_S32K1XX_PM_SPI_ACTIVITY 10 +#endif + +#if defined(CONFIG_PM) +#ifndef PM_IDLE_DOMAIN +# define PM_IDLE_DOMAIN 0 /* Revisit */ +#endif +#endif + +#if defined(CONFIG_PM_SPI0_STANDBY) || defined(CONFIG_PM_SPI0_SLEEP) +# define CONFIG_PM_SPI0 +#endif +#if defined(CONFIG_PM_SPI1_STANDBY) || defined(CONFIG_PM_SPI1_SLEEP) +# define CONFIG_PM_SPI1 +#endif + /**************************************************************************** * Private Types ****************************************************************************/ @@ -177,6 +196,13 @@ static void s32k1xx_lpspi_recvblock(FAR struct spi_dev_s *dev, size_t nwords); #endif +#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 + /* Initialization */ static void @@ -324,6 +350,14 @@ static struct s32k1xx_lpspidev_s g_lpspi2dev = }; #endif +#ifdef CONFIG_PM +static struct pm_callback_s g_spi1_pmcb = +{ + .notify = up_pm_notify, + .prepare = up_pm_prepare, +}; +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -896,6 +930,8 @@ static int s32k1xx_lpspi_lock(FAR struct spi_dev_s *dev, bool lock) FAR struct s32k1xx_lpspidev_s *priv = (FAR struct s32k1xx_lpspidev_s *)dev; int ret; + /* It could be that this needs to be disabled for low level debugging */ + if (lock) { ret = nxsem_wait_uninterruptible(&priv->exclsem); @@ -1376,6 +1412,12 @@ static void s32k1xx_lpspi_exchange_nodma(FAR struct spi_dev_s *dev, spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords); +#if defined(CONFIG_PM) && CONFIG_S32K1XX_PM_SPI_ACTIVITY > 0 + /* Report serial activity to the power management logic */ + + pm_activity(PM_IDLE_DOMAIN, CONFIG_S32K1XX_PM_SPI_ACTIVITY); +#endif + /* bit mode? */ framesize = s32k1xx_lpspi_9to16bitmode(priv); @@ -1742,6 +1784,388 @@ static void s32k1xx_lpspi_bus_initialize(struct s32k1xx_lpspidev_s *priv) s32k1xx_lpspi_modifyreg32(priv, S32K1XX_LPSPI_CR_OFFSET, 0, LPSPI_CR_MEN); } +/**************************************************************************** + * 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) +{ +# ifdef CONFIG_PM_SPI0 + + FAR struct s32k1xx_lpspidev_s *priv0 = NULL; + + /* make the priv1ate struct for lpspi bus 0 */ + + priv0 = &g_lpspi0dev; + +# endif +# ifdef CONFIG_PM_SPI1 + + FAR struct s32k1xx_lpspidev_s *priv1 = NULL; + + /* make the priv1ate struct for lpspi bus 1 */ + + priv1 = &g_lpspi1dev; + +# endif + + unsigned int count = 0; /* the amount of peripheral clocks to change */ + + peripheral_clock_source_t clock_source; + + /* check if the transition is from the IDLE domain to the NORMAL domain */ + + /* or the mode is already done */ + + if (((pm_querystate(PM_IDLE_DOMAIN) == PM_IDLE) && + (pmstate == PM_NORMAL)) || + (((pm_querystate(PM_IDLE_DOMAIN) == pmstate)))) + { + /* return */ + + return; + } + + /* check which PM it is */ + + switch (pmstate) + { + /* in case it needs to change to the RUN mode */ + + case PM_NORMAL: + { + /* Logic for PM_NORMAL goes here */ + + /* set the right clock source to go back to RUN mode */ + + clock_source = CLK_SRC_SPLL_DIV2; + +# ifdef CONFIG_PM_SPI0 + + /* add 1 to count to do it for SPI0 */ + + count++; +# endif + +# ifdef CONFIG_PM_SPI1 + + /* add 1 to count to do it for SPI1 */ + + count++; +# endif + } + break; + + default: + { + /* don't do anything, just return OK */ + } + break; + } + + /* check if the LPSPI needs to change */ + + if (count) + { + /* make the peripheral clock config struct */ + + const struct peripheral_clock_config_s clock_config[] = + { +# ifdef CONFIG_PM_SPI0 + + { + .clkname = LPSPI0_CLK, + .clkgate = true, + .clksrc = clock_source, + .frac = MULTIPLY_BY_ONE, + .divider = 1, + }, +# endif +# ifdef CONFIG_PM_SPI1 + + { + .clkname = LPSPI1_CLK, + .clkgate = true, + .clksrc = clock_source, + .frac = MULTIPLY_BY_ONE, + .divider = 1, + } +# endif + }; + +# ifdef CONFIG_PM_SPI0 + + /* disable LPSP0 */ + + s32k1xx_lpspi_modifyreg32(priv0, S32K1XX_LPSPI_CR_OFFSET, 0, + !LPSPI_CR_MEN); + +# endif +# ifdef CONFIG_PM_SPI1 + + /* disable LPSPI */ + + s32k1xx_lpspi_modifyreg32(priv1, S32K1XX_LPSPI_CR_OFFSET, 0, + !LPSPI_CR_MEN); + +# endif + + /* change the clock config for the new mode */ + + s32k1xx_periphclocks(count, clock_config); + +# ifdef CONFIG_PM_SPI0 + + /* Enable LPSP0 */ + + s32k1xx_lpspi_modifyreg32(priv0, S32K1XX_LPSPI_CR_OFFSET, 0, + LPSPI_CR_MEN); + +# endif +# ifdef CONFIG_PM_SPI1 + + /* Enable LPSPI */ + + s32k1xx_lpspi_modifyreg32(priv1, S32K1XX_LPSPI_CR_OFFSET, 0, + LPSPI_CR_MEN); +# endif + + /* get the clock freq */ + } + + /* return */ + + return; +} +#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. */ + +# ifdef CONFIG_PM_SPI0 + FAR struct s32k1xx_lpspidev_s *priv0 = NULL; + + /* make the private struct for lpspi bus 0 */ + + priv0 = &g_lpspi0dev; +# endif +# ifdef CONFIG_PM_SPI1 + FAR struct s32k1xx_lpspidev_s *priv1 = NULL; + + /* make the private struct for lpspi bus 1 */ + + priv1 = &g_lpspi1dev; +# endif + + unsigned int count = 0; /* the amount of peripheral clocks to change */ + + peripheral_clock_source_t clock_source; + + /* check if the transition to the mode is already done */ + + if (pm_querystate(PM_IDLE_DOMAIN) == pmstate) + { + /* return */ + + return OK; + } + + /* check which PM it is */ + + switch (pmstate) + { + /* in case it needs to prepare for VLPR mode */ + + case PM_STANDBY: + { + /* Logic for PM_STANDBY goes here */ + + /* set the right clock source */ + + clock_source = CLK_SRC_SIRC_DIV2; + +# ifdef CONFIG_PM_SPI0_STANDBY + + /* increase count to change the SPI0 */ + + count++; + +# endif +# ifdef CONFIG_PM_SPI1_STANDBY + + /* increase count to change the SPI1 */ + + count++; + +# endif + } + break; + + /* in case it needs to prepare for VLPR mode */ + + case PM_SLEEP: + { + /* Logic for PM_STANDBY goes here */ + + /* set the right clock source */ + + clock_source = CLK_SRC_SIRC_DIV2; + +# ifdef CONFIG_PM_SPI0_SLEEP + + /* increase count to change the SPI0 */ + + count++; + +# endif +# ifdef CONFIG_PM_SPI1_SLEEP + + /* increase count to change the SPI1 */ + + count++; + +# endif + } + break; + + default: + { + /* don't do anything, just return OK */ + } + break; + } + + /* check if you need to change something */ + + if (count) + { + /* make the peripheral clock config struct */ + + const struct peripheral_clock_config_s clock_config[] = + { +# ifdef CONFIG_PM_SPI0 + { + .clkname = LPSPI0_CLK, + .clkgate = true, + .clksrc = clock_source, + .frac = MULTIPLY_BY_ONE, + .divider = 1, + }, +# endif +# ifdef CONFIG_PM_SPI1 + { + .clkname = LPSPI1_CLK, + .clkgate = true, + .clksrc = clock_source, + .frac = MULTIPLY_BY_ONE, + .divider = 1, + } +# endif + }; + +# ifdef CONFIG_PM_SPI0 + + /* disable LPSPI0 */ + + s32k1xx_lpspi_modifyreg32(priv0, S32K1XX_LPSPI_CR_OFFSET, 0, + !LPSPI_CR_MEN); + +# endif +# ifdef CONFIG_PM_SPI1 + + /* disable LPSPI1 */ + + s32k1xx_lpspi_modifyreg32(priv1, S32K1XX_LPSPI_CR_OFFSET, 0, + !LPSPI_CR_MEN); + +# endif + + /* change the clock config for the new mode */ + + s32k1xx_periphclocks(count, clock_config); + +# ifdef CONFIG_PM_SPI0 + + /* Enable LPSPI */ + + s32k1xx_lpspi_modifyreg32(priv0, S32K1XX_LPSPI_CR_OFFSET, 0, + LPSPI_CR_MEN); + +# endif +# ifdef CONFIG_PM_SPI1 + + /* Enable LPSPI */ + + s32k1xx_lpspi_modifyreg32(priv1, S32K1XX_LPSPI_CR_OFFSET, 0, + LPSPI_CR_MEN); + +# endif + } + + /* get the clock freq */ + + /* return OK */ + + return OK; +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -1794,6 +2218,17 @@ FAR struct spi_dev_s *s32k1xx_lpspibus_initialize(int bus) #ifdef CONFIG_S32K1XX_LPSPI1 if (bus == 1) { + #ifdef CONFIG_PM + #if defined(CONFIG_PM_SPI_STANDBY) || defined(CONFIG_PM_SPI_SLEEP) + int ret; + + /* Register to receive power management callbacks */ + + ret = pm_register(&g_spi1_pmcb); + DEBUGASSERT(ret == OK); + UNUSED(ret); + #endif + #endif /* Select SPI1 */ priv = &g_lpspi1dev; diff --git a/arch/arm/src/s32k1xx/s32k1xx_periphclocks.c b/arch/arm/src/s32k1xx/s32k1xx_periphclocks.c index c05b61896d2..c5c0d1581e5 100644 --- a/arch/arm/src/s32k1xx/s32k1xx_periphclocks.c +++ b/arch/arm/src/s32k1xx/s32k1xx_periphclocks.c @@ -106,29 +106,6 @@ static uint32_t *s32k1xx_get_pclkctrl(enum clock_names_e clkname) return NULL; } -/**************************************************************************** - * Name: s32k1xx_pclk_disable - * - * Description: - * This function enables/disables the clock for a given peripheral. - * - * Input Parameters: - * clkname - The name of the peripheral clock to be disabled - * enable - true: Enable the peripheral clock. - * - * Returned Value: - * None - * - ****************************************************************************/ - -static void s32k1xx_pclk_disable(enum clock_names_e clkname) -{ - uint32_t *ctrlp = s32k1xx_get_pclkctrl(clkname); - DEBUGASSERT(ctrlp != NULL); - - *ctrlp &= ~PCC_CGC; -} - /**************************************************************************** * Name: s32k1xx_set_pclkctrl * @@ -279,7 +256,7 @@ void s32k1xx_periphclocks(unsigned int count, { /* Disable the peripheral clock */ - s32k1xx_pclk_disable(pclks->clkname); + s32k1xx_pclk_enable(pclks->clkname, false); /* Set peripheral clock control */ @@ -398,3 +375,39 @@ int s32k1xx_get_pclkfreq(enum clock_names_e clkname, uint32_t *frequency) return ret; } + +/**************************************************************************** + * Name: s32k1xx_pclk_enable + * + * Description: + * This function enables/disables the clock for a given peripheral. + * + * Input Parameters: + * clkname - The name of the peripheral clock to be disabled + * enable - true: Enable the peripheral clock. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void s32k1xx_pclk_enable(enum clock_names_e clkname, bool enable) +{ + uint32_t *ctrlp = s32k1xx_get_pclkctrl(clkname); + DEBUGASSERT(ctrlp != NULL); + + /* check if it needs to be enabled */ + + if (enable) + { + /* enable it */ + + *ctrlp |= PCC_CGC; + } + else + { + /* disable it */ + + *ctrlp &= ~PCC_CGC; + } +} diff --git a/arch/arm/src/s32k1xx/s32k1xx_periphclocks.h b/arch/arm/src/s32k1xx/s32k1xx_periphclocks.h index 028a99a6109..604414e161d 100644 --- a/arch/arm/src/s32k1xx/s32k1xx_periphclocks.h +++ b/arch/arm/src/s32k1xx/s32k1xx_periphclocks.h @@ -267,6 +267,23 @@ void s32k1xx_periphclocks(unsigned int count, int s32k1xx_get_pclkfreq(enum clock_names_e clkname, uint32_t *frequency); +/**************************************************************************** + * Name: s32k1xx_pclk_enable + * + * Description: + * This function enables/disables the clock for a given peripheral. + * + * Input Parameters: + * clkname - The name of the peripheral clock to be disabled + * enable - true: Enable the peripheral clock. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void s32k1xx_pclk_enable(enum clock_names_e clkname, bool enable); + #undef EXTERN #if defined(__cplusplus) } diff --git a/arch/arm/src/s32k1xx/s32k1xx_pminitialize.c b/arch/arm/src/s32k1xx/s32k1xx_pminitialize.c new file mode 100644 index 00000000000..096d6bbc20a --- /dev/null +++ b/arch/arm/src/s32k1xx/s32k1xx_pminitialize.c @@ -0,0 +1,62 @@ +/**************************************************************************** + * arch/arm/src/s32k1xx/s32k1xx_pminitialize.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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include "arm_internal.h" + +#ifdef CONFIG_PM + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: arm_pminitialize + * + * Description: + * This function is called by MCU-specific logic at power-on reset in + * order to provide one-time initialization the power management subsystem. + * This function must be called *very* early in the initialization sequence + * *before* any other device drivers are initialized (since they may + * attempt to register with the power management subsystem). + * + * Input Parameters: + * None. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void arm_pminitialize(void) +{ + /* Then initialize the NuttX power management subsystem proper */ + + pm_initialize(); +} + +#endif /* CONFIG_PM */ diff --git a/arch/arm/src/s32k1xx/s32k1xx_serial.c b/arch/arm/src/s32k1xx/s32k1xx_serial.c index 6f8c37933aa..63724a611d5 100644 --- a/arch/arm/src/s32k1xx/s32k1xx_serial.c +++ b/arch/arm/src/s32k1xx/s32k1xx_serial.c @@ -57,6 +57,8 @@ #include "s32k1xx_pin.h" #include "s32k1xx_lowputc.h" +#include "s32k1xx_periphclocks.h" + #ifdef USE_SERIALDRIVER /**************************************************************************** @@ -132,9 +134,20 @@ #endif #if defined(CONFIG_PM) +#ifndef PM_IDLE_DOMAIN # define PM_IDLE_DOMAIN 0 /* Revisit */ #endif +#endif +#if defined(CONFIG_PM_SERIAL0_STANDBY) || defined(CONFIG_PM_SERIAL0_SLEEP) +# define CONFIG_PM_SERIAL0 +#endif +#if defined(CONFIG_PM_SERIAL1_STANDBY) || defined(CONFIG_PM_SERIAL1_SLEEP) +# define CONFIG_PM_SERIAL1 +#endif +#if defined(CONFIG_PM_SERIAL2_STANDBY) || defined(CONFIG_PM_SERIAL2_SLEEP) +# define CONFIG_PM_SERIAL2 +#endif /**************************************************************************** * Private Types ****************************************************************************/ @@ -508,8 +521,14 @@ static int s32k1xx_setup(struct uart_dev_s *dev) config.invrts = priv->inviflow; /* Inversion of outbound flow control */ #endif + /* configure the LPUART */ + ret = s32k1xx_lpuart_configure(priv->uartbase, &config); + /* get the current interrupt bits and place them in ie */ + + /* (used to use the interrupts) */ + priv->ie = s32k1xx_serialin(priv, S32K1XX_LPUART_CTRL_OFFSET) & \ LPUART_ALL_INTS; return ret; @@ -536,7 +555,13 @@ static void s32k1xx_shutdown(struct uart_dev_s *dev) /* Disable the UART */ + /* set the reset bit */ + s32k1xx_serialout(priv, S32K1XX_LPUART_GLOBAL_OFFSET, LPUART_GLOBAL_RST); + + /* clear the reset bit again */ + + s32k1xx_serialout(priv, S32K1XX_LPUART_GLOBAL_OFFSET, 0); } /**************************************************************************** @@ -1114,38 +1139,205 @@ static bool s32k1xx_txempty(struct uart_dev_s *dev) static void up_pm_notify(struct pm_callback_s *cb, int domain, enum pm_state_e pmstate) { - switch (pmstate) + unsigned int count = 0; /* the amount of peripheral clocks to change */ + + peripheral_clock_source_t clock_source; + + #ifdef CONFIG_PM_SERIAL0 + struct s32k1xx_uart_s *priv0 = g_uart0port.priv; + #endif + #ifdef CONFIG_PM_SERIAL1 + struct s32k1xx_uart_s *priv1 = g_uart1port.priv; + #endif + #ifdef CONFIG_PM_SERIAL2 + struct s32k1xx_uart_s *priv2 = g_uart2port.priv; + #endif + + uint32_t ret_reg = 0; + + /* check if the transition is from the IDLE domain to the NORMAL domain */ + + /* or the mode is already done */ + + if (((pm_querystate(PM_IDLE_DOMAIN) == PM_IDLE) && + (pmstate == PM_NORMAL)) || + (((pm_querystate(PM_IDLE_DOMAIN) == pmstate)))) { - case(PM_NORMAL): - { - /* Logic for PM_NORMAL goes here */ - } - break; + /* return */ - case(PM_IDLE): - { - /* Logic for PM_IDLE goes here */ - } - break; - - case(PM_STANDBY): - { - /* Logic for PM_STANDBY goes here */ - } - break; - - case(PM_SLEEP): - { - /* Logic for PM_SLEEP goes here */ - } - break; - - default: - - /* Should not get here */ - - break; + return; } + + /* check which PM it is */ + + switch (pmstate) + { + /* in case it needs to change to the RUN mode */ + + case PM_NORMAL: + { + /* Logic for PM_NORMAL goes here */ + + /* set the right clock source to go back to RUN mode */ + + clock_source = CLK_SRC_SPLL_DIV2; + + count = 1; + } + break; + default: + { + /* don't do anything, just return OK */ + } + break; + } + + /* check if something needs to change */ + + if (count) + { + #ifdef CONFIG_PM_SERIAL0 + + /* make the peripheral clock config struct */ + + const struct peripheral_clock_config_s clock_config0 = + { + .clkname = LPUART0_CLK, + .clkgate = true, + .clksrc = clock_source, + .frac = MULTIPLY_BY_ONE, + .divider = 1, + }; + + /* read the FIFO register */ + + ret_reg = getreg32(priv0->uartbase + S32K1XX_LPUART_FIFO_OFFSET); + + /* make the value */ + + ret_reg |= (LPUART_FIFO_RXFLUSH + LPUART_FIFO_TXFLUSH); + + /* write the new value */ + + putreg32(ret_reg, priv0->uartbase + S32K1XX_LPUART_FIFO_OFFSET); + + /* shutdown the LPUART1 (soft reset) */ + + s32k1xx_shutdown(&g_uart0port); + + /* change the clock config for the new mode */ + + s32k1xx_periphclocks(count, &clock_config0); + + /* shutdown the LPUART1 (soft reset) */ + + s32k1xx_shutdown(&g_uart0port); + + /* set up the LPUART1 again for the new mode */ + + s32k1xx_setup(&g_uart0port); + + /* enable the interrupts */ + + s32k1xx_rxint(&g_uart0port, true); + s32k1xx_txint(&g_uart0port, true); + + #endif + #ifdef CONFIG_PM_SERIAL1 + + /* make the peripheral clock config struct */ + + const struct peripheral_clock_config_s clock_config1 = + { + .clkname = LPUART1_CLK, + .clkgate = true, + .clksrc = clock_source, + .frac = MULTIPLY_BY_ONE, + .divider = 1, + }; + + /* read the FIFO register */ + + ret_reg = getreg32(priv1->uartbase + S32K1XX_LPUART_FIFO_OFFSET); + + /* make the value */ + + ret_reg |= (LPUART_FIFO_RXFLUSH + LPUART_FIFO_TXFLUSH); + + /* write the new value */ + + putreg32(ret_reg, priv1->uartbase + S32K1XX_LPUART_FIFO_OFFSET); + + /* shutdown the LPUART1 (soft reset) */ + + s32k1xx_shutdown(&g_uart1port); + + /* change the clock config for the new mode */ + + s32k1xx_periphclocks(count, &clock_config1); + + /* shutdown the LPUART1 (soft reset) */ + + s32k1xx_shutdown(&g_uart1port); + + /* set up the LPUART1 again for the new mode */ + + s32k1xx_setup(&g_uart1port); + + /* enable the interrupts */ + + s32k1xx_rxint(&g_uart1port, true); + s32k1xx_txint(&g_uart1port, true); + + #endif + #ifdef CONFIG_PM_SERIAL2 + + /* make the peripheral clock config struct */ + + const struct peripheral_clock_config_s clock_config2 = + { + .clkname = LPUART2_CLK, + .clkgate = true, + .clksrc = clock_source, + .frac = MULTIPLY_BY_ONE, + .divider = 1, + }; + + /* read the FIFO register */ + + ret_reg = getreg32(priv2->uartbase + S32K1XX_LPUART_FIFO_OFFSET); + + /* make the value */ + + ret_reg |= (LPUART_FIFO_RXFLUSH + LPUART_FIFO_TXFLUSH); + + /* write the new value */ + + putreg32(ret_reg, priv2->uartbase + S32K1XX_LPUART_FIFO_OFFSET); + + /* shutdown the LPUART1 (soft reset) */ + + s32k1xx_shutdown(&g_uart2port); + + /* change the clock config for the new mode */ + + s32k1xx_periphclocks(count, &clock_config2); + + /* shutdown the LPUART1 (soft reset) */ + + s32k1xx_shutdown(&g_uart2port); + + /* set up the LPUART1 again for the new mode */ + + s32k1xx_setup(&g_uart2port); + + /* enable the interrupts */ + + s32k1xx_rxint(&g_uart2port, true); + s32k1xx_txint(&g_uart2port, true); + + #endif + } } #endif @@ -1189,6 +1381,209 @@ static int up_pm_prepare(struct pm_callback_s *cb, int domain, { /* Logic to prepare for a reduced power state goes here. */ + unsigned int count = 1; /* the amount of peripheral clocks to change */ + + peripheral_clock_source_t clock_source; + + #ifdef CONFIG_PM_SERIAL0 + struct s32k1xx_uart_s *priv0 = (struct s32k1xx_uart_s *)g_uart0port.priv; + #endif + #ifdef CONFIG_PM_SERIAL1 + struct s32k1xx_uart_s *priv1 = (struct s32k1xx_uart_s *)g_uart1port.priv; + #endif + #ifdef CONFIG_PM_SERIAL2 + struct s32k1xx_uart_s *priv2 = (struct s32k1xx_uart_s *)g_uart2port.priv; + #endif + + uint32_t ret_reg = 0; + + /* check if the transition to the mode is already done */ + + if (pm_querystate(PM_IDLE_DOMAIN) == pmstate) + { + /* return */ + + return OK; + } + + /* check which PM it is */ + + switch (pmstate) + { + /* in case it needs to prepare for VLPR mode */ + + case PM_STANDBY: + { + /* Logic for PM_STANDBY goes here */ + + /* set the right clock source */ + + clock_source = CLK_SRC_SIRC_DIV2; + } + break; + + /* in case it needs to prepare for sleep mode */ + + case PM_SLEEP: + { + /* Logic for PM_SLEEP goes here */ + + /* set the right clock source */ + + clock_source = CLK_SRC_SIRC_DIV2; + } + break; + default: + { + /* don't do anything, just return OK */ + + return OK; + } + break; + } + + #ifdef CONFIG_PM_SERIAL0 + + /* make the peripheral clock config struct */ + + const struct peripheral_clock_config_s clock_config0 = + { + .clkname = LPUART0_CLK, + .clkgate = true, + .clksrc = clock_source, + .frac = MULTIPLY_BY_ONE, + .divider = 1, + }; + + /* read the FIFO register */ + + ret_reg = getreg32(priv0->uartbase + S32K1XX_LPUART_FIFO_OFFSET); + + /* make the value */ + + ret_reg |= (LPUART_FIFO_RXFLUSH + LPUART_FIFO_TXFLUSH); + + /* write the new value */ + + putreg32(ret_reg, priv0->uartbase + S32K1XX_LPUART_FIFO_OFFSET); + + /* shutdown the LPUART1 (soft reset) */ + + s32k1xx_shutdown(&g_uart0port); + + /* change the clock config for the new mode */ + + s32k1xx_periphclocks(count, &clock_config0); + + /* shutdown the LPUART1 (soft reset) */ + + s32k1xx_shutdown(&g_uart0port); + + /* set up the LPUART1 again for the new mode */ + + s32k1xx_setup(&g_uart0port); + + /* enable the interrupts */ + + s32k1xx_rxint(&g_uart0port, true); + s32k1xx_txint(&g_uart0port, true); + + #endif + #ifdef CONFIG_PM_SERIAL1 + + /* make the peripheral clock config struct */ + + const struct peripheral_clock_config_s clock_config1 = + { + .clkname = LPUART1_CLK, + .clkgate = true, + .clksrc = clock_source, + .frac = MULTIPLY_BY_ONE, + .divider = 1, + }; + + /* read the FIFO register */ + + ret_reg = getreg32(priv1->uartbase + S32K1XX_LPUART_FIFO_OFFSET); + + /* make the value */ + + ret_reg |= (LPUART_FIFO_RXFLUSH + LPUART_FIFO_TXFLUSH); + + /* write the new value */ + + putreg32(ret_reg, priv1->uartbase + S32K1XX_LPUART_FIFO_OFFSET); + + /* shutdown the LPUART1 (soft reset) */ + + s32k1xx_shutdown(&g_uart1port); + + /* change the clock config for the new mode */ + + s32k1xx_periphclocks(count, &clock_config1); + + /* shutdown the LPUART1 (soft reset) */ + + s32k1xx_shutdown(&g_uart1port); + + /* set up the LPUART1 again for the new mode */ + + s32k1xx_setup(&g_uart1port); + + /* enable the interrupts */ + + s32k1xx_rxint(&g_uart1port, true); + s32k1xx_txint(&g_uart1port, true); + + #endif + #ifdef CONFIG_PM_SERIAL2 + + /* make the peripheral clock config struct */ + + const struct peripheral_clock_config_s clock_config2 = + { + .clkname = LPUART2_CLK, + .clkgate = true, + .clksrc = clock_source, + .frac = MULTIPLY_BY_ONE, + .divider = 1, + }; + + /* read the FIFO register */ + + ret_reg = getreg32(priv2->uartbase + S32K1XX_LPUART_FIFO_OFFSET); + + /* make the value */ + + ret_reg |= (LPUART_FIFO_RXFLUSH + LPUART_FIFO_TXFLUSH); + + /* write the new value */ + + putreg32(ret_reg, priv2->uartbase + S32K1XX_LPUART_FIFO_OFFSET); + + /* shutdown the LPUART1 (soft reset) */ + + s32k1xx_shutdown(&g_uart2port); + + /* change the clock config for the new mode */ + + s32k1xx_periphclocks(count, &clock_config2); + + /* shutdown the LPUART1 (soft reset) */ + + s32k1xx_shutdown(&g_uart2port); + + /* set up the LPUART1 again for the new mode */ + + s32k1xx_setup(&g_uart2port); + + /* enable the interrupts */ + + s32k1xx_rxint(&g_uart2port, true); + s32k1xx_txint(&g_uart2port, true); + + #endif + return OK; } #endif @@ -1236,6 +1631,8 @@ void s32k1xx_earlyserialinit(void) void arm_serialinit(void) { #ifdef CONFIG_PM + #if defined(CONFIG_PM_SERIAL_STANDBY) || defined(CONFIG_PM_SERIAL_SLEEP) + int ret; /* Register to receive power management callbacks */ @@ -1243,6 +1640,7 @@ void arm_serialinit(void) ret = pm_register(&g_serial_pmcb); DEBUGASSERT(ret == OK); UNUSED(ret); + #endif #endif #ifdef CONSOLE_DEV