S32K1xx: added PM support

This commit is contained in:
Cis van Mierlo
2021-09-14 13:28:04 +02:00
committed by Alan Carvalho de Assis
parent 49667a230e
commit b316c679c1
10 changed files with 1974 additions and 126 deletions
+5 -1
View File
@@ -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
@@ -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 */
+7 -6
View File
@@ -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 */
File diff suppressed because it is too large Load Diff
@@ -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
*
+435
View File
@@ -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;
+37 -24
View File
@@ -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;
}
}
@@ -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)
}
@@ -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 <nuttx/config.h>
#include <nuttx/power/pm.h>
#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 */
File diff suppressed because it is too large Load Diff