soc/arm/silabs_exx32: fix PM implementation - wake up using BURTC timer

- Add Gecko BURTC sys_clock driver to handle wake up from EM2,3 states
- Remove custom PM policy and dependency on HAL sl_power_manager service
- EM1 supported in all configurations
- EM2,3 supported only if SysTick is replaced by BURTC

Signed-off-by: Roman Dobrodii <rdobrodii@antmicro.com>
This commit is contained in:
Roman Dobrodii 2023-04-05 17:51:04 +02:00 committed by Carles Cufí
commit cb14d8b099
21 changed files with 491 additions and 136 deletions

View file

@ -21,6 +21,18 @@ config SOC_PART_NUMBER
that you should not set directly. The part number selection choice defines
the default value for this string.
config SOC_GECKO_SERIES2
bool
help
Set if we're building for Gecko Series 2 SoC.
This is equivalent of _SILICON_LABS_32B_SERIES_2 definition in HAL
code.
config SOC_GECKO_BURTC
bool
help
Set if the Back-Up Real Time Counter (BURTC) HAL module is used.
config SOC_GECKO_CORE
bool
default y
@ -163,6 +175,12 @@ config SOC_GECKO_CMU
if SOC_GECKO_CMU
config CMU_NEED_LFXO
bool
help
Set if LFXO oscillator should be configured and enabled, potentially
in on-demand mode, after SoC is initialized.
choice
prompt "High Frequency Clock Selection"
default CMU_HFCLK_HFXO
@ -175,6 +193,7 @@ config CMU_HFCLK_HFXO
config CMU_HFCLK_LFXO
bool "External low frequency crystal oscillator"
select CMU_NEED_LFXO
help
Set this option to use the external low frequency crystal oscillator
as high frequency clock.
@ -186,6 +205,37 @@ config CMU_HFCLK_HFRCO
endchoice
choice
prompt "BURTC Clock Selection"
depends on SOC_GECKO_BURTC
default CMU_BURTCCLK_LFRCO
config CMU_BURTCCLK_LFXO
bool "LFXO - external low frequency crystal oscillator"
select CMU_NEED_LFXO
help
Set this option to use LFXO - the external low freqency crystal oscillator
as BURTC clock.
Frequency is set by external crystal, typically 32.768 kHz.
config CMU_BURTCCLK_LFRCO
bool "LFRCO - internal low frequency RC oscillator"
help
Set this option to use LFRCO - the internal low freqency RC oscillator
as BURTC clock.
Frequency is approximately 32.768 kHz.
config CMU_BURTCCLK_ULFRCO
bool "ULFRCO - internal ultra low frequency RC oscillator"
help
Set this option to use ULFRCO - the external low freqency crystal oscillator
as BURTC clock.
Frequency is approximately 1 kHz.
endchoice
config CMU_HFXO_FREQ
int "External high frequency oscillator frequency"
help

View file

@ -9,4 +9,7 @@ config SOC_GECKO_EMU
select SOC_GECKO_CORE
depends on PM
config CORTEX_M_SYSTICK
default n if GECKO_BURTC_TIMER
endif

View file

@ -41,12 +41,44 @@ LOG_MODULE_REGISTER(soc, CONFIG_SOC_LOG_LEVEL);
* @brief Initialization parameters for the external high frequency oscillator
*/
static CMU_HFXOInit_TypeDef hfxoInit = CMU_HFXOINIT_DEFAULT;
#elif (defined CONFIG_CMU_HFCLK_LFXO)
#endif
#ifdef CONFIG_CMU_NEED_LFXO
/**
* @brief Initialization parameters for the external low frequency oscillator
*/
static CMU_LFXOInit_TypeDef lfxoInit = CMU_LFXOINIT_DEFAULT;
#endif
static void init_lfxo(void)
{
/*
* Configuring LFXO disables it, so we can do that only if it's not
* used as a SYSCLK/HFCLK source.
*/
#if defined(_SILICON_LABS_32B_SERIES_2)
if (CMU_ClockSelectGet(cmuClock_SYSCLK) != cmuSelect_LFXO) {
/*
* Check if device has LFXO configuration info in DEVINFO
* See AN0016.2
*/
if ((DEVINFO->MODULEINFO & DEVINFO_MODULEINFO_LFXOCALVAL) ==
DEVINFO_MODULEINFO_LFXOCALVAL_VALID) {
lfxoInit.capTune =
(DEVINFO->MODXOCAL & _DEVINFO_MODXOCAL_LFXOCAPTUNE_MASK) >>
_DEVINFO_MODXOCAL_LFXOCAPTUNE_SHIFT;
}
CMU_LFXOInit(&lfxoInit);
}
#else
if (CMU_ClockSelectGet(cmuClock_HF) != cmuSelect_LFXO) {
CMU_LFXOInit(&lfxoInit);
CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
}
#endif /* _SILICON_LABS_32B_SERIES_2 */
SystemLFXOClockSet(CONFIG_CMU_LFXO_FREQ);
}
#endif /* CONFIG_CMU_NEED_LFXO */
/**
* @brief Initialize the system clock
@ -85,29 +117,11 @@ static ALWAYS_INLINE void clock_init(void)
CMU_OscillatorEnable(cmuOsc_HFRCO, false, false);
#endif /* _SILICON_LABS_32B_SERIES_2 */
#elif (defined CONFIG_CMU_HFCLK_LFXO)
/* LFXO should've been already brought up by init_lfxo() */
#if defined(_SILICON_LABS_32B_SERIES_2)
if (CMU_ClockSelectGet(cmuClock_SYSCLK) != cmuSelect_LFXO) {
/*
* Start the LFXO Oscillator as well (use by RTCC)
* Check if device has HFXO configuration info in DEVINFO
* See AN0016.2
*/
if ((DEVINFO->MODULEINFO & DEVINFO_MODULEINFO_LFXOCALVAL) ==
DEVINFO_MODULEINFO_LFXOCALVAL_VALID) {
lfxoInit.capTune =
(DEVINFO->MODXOCAL & _DEVINFO_MODXOCAL_LFXOCAPTUNE_MASK) >>
_DEVINFO_MODXOCAL_LFXOCAPTUNE_SHIFT;
}
}
SystemLFXOClockSet(CONFIG_CMU_LFXO_FREQ);
CMU_ClockSelectSet(cmuClock_SYSCLK, cmuSelect_LFXO);
#else
if (CMU_ClockSelectGet(cmuClock_HF) != cmuSelect_LFXO) {
CMU_LFXOInit(&lfxoInit);
CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
CMU_ClockSelectSet(cmuClock_HF, cmuSelect_LFXO);
}
SystemLFXOClockSet(CONFIG_CMU_LFXO_FREQ);
CMU_ClockSelectSet(cmuClock_HF, cmuSelect_LFXO);
CMU_OscillatorEnable(cmuOsc_HFRCO, false, false);
#endif /* _SILICON_LABS_32B_SERIES_2 */
#elif (defined CONFIG_CMU_HFCLK_HFRCO)
@ -204,6 +218,10 @@ static int silabs_exx32_init(void)
/* handle chip errata */
CHIP_Init();
#ifdef CONFIG_CMU_NEED_LFXO
init_lfxo();
#endif
#ifdef CONFIG_SOC_GECKO_DEV_INIT
sl_device_init_dcdc();
sl_device_init_hfxo();
@ -215,7 +233,7 @@ static int silabs_exx32_init(void)
sl_hfxo_manager_init();
#endif
#else
#else /* !CONFIG_SOC_GECKO_DEV_INIT */
#ifdef CONFIG_SOC_GECKO_EMU_DCDC
dcdc_init();
@ -234,7 +252,8 @@ static int silabs_exx32_init(void)
/* Configure SWO debug output */
swo_init();
#endif
#endif
#endif /* !CONFIG_SOC_GECKO_DEV_INIT */
/* restore interrupt state */
irq_unlock(oldLevel);
return 0;

View file

@ -9,25 +9,14 @@
#include <zephyr/pm/pm.h>
#include <em_emu.h>
#include <zephyr/device.h>
#include <zephyr/drivers/counter.h>
LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL);
#ifdef CONFIG_SOC_GECKO_DEV_INIT
#include <sl_power_manager.h>
static const struct device *const counter_dev = DEVICE_DT_GET(DT_NODELABEL(stimer0));
static struct counter_alarm_cfg wakeup_cfg = {
.callback = NULL
};
#endif
/*
* Power state map:
* PM_STATE_RUNTIME_IDLE: EM1 Sleep
* PM_STATE_SUSPEND_TO_IDLE: EM2 Deep Sleep
* PM_STATE_STANDBY: EM3 Stop
* PM_STATE_SOFT_OFF: EM4
*/
/* Invoke Low Power/System Off specific Tasks */
@ -37,52 +26,6 @@ __weak void pm_state_set(enum pm_state state, uint8_t substate_id)
LOG_DBG("SoC entering power state %d", state);
#ifdef CONFIG_SOC_GECKO_DEV_INIT
sl_power_manager_em_t energy_mode;
/* Save RTCC IRQ priority */
uint32_t rtcc_prio = NVIC_GetPriority(RTCC_IRQn);
/*
* When this function is entered the Kernel has disabled handling interrupts
* with priority other than 0. The Interrupt for the timer used to wake up
* the cpu has priority equal to 1. Manually set this priority to 0 so that
* cpu could exit sleep state.
*
* Note on priority value: set priority to -1 because z_arm_irq_priority_set
* offsets the priorities by 1.
* This function call will effectively set the priority to 0.
*/
z_arm_irq_priority_set(RTCC_IRQn, -1, 0);
switch (state) {
case PM_STATE_STANDBY:
energy_mode = SL_POWER_MANAGER_EM3;
/* Limit sleep level to given state */
sl_power_manager_add_em_requirement(energy_mode);
counter_start(counter_dev);
counter_set_channel_alarm(counter_dev, 0, &wakeup_cfg);
sl_power_manager_sleep();
k_cpu_idle();
counter_stop(counter_dev);
/* Remove sleep level limit */
sl_power_manager_remove_em_requirement(energy_mode);
break;
default:
LOG_DBG("Unsupported power state %u", state);
break;
}
LOG_DBG("SoC leaving power state %d", state);
/* Restore RTCC IRQ priority */
z_arm_irq_priority_set(RTCC_IRQn, (rtcc_prio - 1), 0);
#else
/* FIXME: When this function is entered the Kernel has disabled
* interrupts using BASEPRI register. This is incorrect as it prevents
* waking up from any interrupt which priority is not 0. Work around the
@ -105,6 +48,9 @@ __weak void pm_state_set(enum pm_state state, uint8_t substate_id)
case PM_STATE_STANDBY:
EMU_EnterEM3(true);
break;
case PM_STATE_SOFT_OFF:
EMU_EnterEM4();
break;
default:
LOG_DBG("Unsupported power state %u", state);
break;
@ -114,8 +60,6 @@ __weak void pm_state_set(enum pm_state state, uint8_t substate_id)
/* Clear PRIMASK */
__enable_irq();
#endif
}
/* Handle SOC specific activity after Low Power Mode Exit */
@ -124,37 +68,3 @@ __weak void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id)
ARG_UNUSED(state);
ARG_UNUSED(substate_id);
}
#if defined(CONFIG_SOC_GECKO_DEV_INIT) && defined(CONFIG_PM_POLICY_CUSTOM)
/* CONFIG_PM_POLICY_CUSTOM allows us to set the next alarm to a specific number
* of ticks in the future. This is needed for the Gecko SleepTimer to wake up
* the device properly.
*/
static const struct pm_state_info pm_min_residency[] =
PM_STATE_INFO_LIST_FROM_DT_CPU(DT_NODELABEL(cpu0));
struct pm_state_info pm_state_active = {PM_STATE_ACTIVE, 0, 0, 0};
__weak struct pm_state_info *pm_policy_next_state(uint8_t cpu, int32_t ticks)
{
int i;
for (i = ARRAY_SIZE(pm_min_residency) - 1; i >= 0; i--) {
if ((ticks == K_TICKS_FOREVER) ||
(ticks >= k_us_to_ticks_ceil32(
pm_min_residency[i].min_residency_us))) {
LOG_DBG("Selected power state %d "
"(ticks: %d, min_residency: %u)",
pm_min_residency[i].state, ticks,
pm_min_residency[i].min_residency_us);
/* Busy waiting for 1 tick to flush UART buffer */
k_busy_wait(k_ticks_to_us_floor32(1));
wakeup_cfg.ticks = ticks;
return ((struct pm_state_info *) &pm_min_residency[i]);
}
}
return (struct pm_state_info *)(&pm_state_active);
}
#endif

View file

@ -19,11 +19,6 @@ config PM
select COUNTER
select UART_INTERRUPT_DRIVEN
choice PM_POLICY
default PM_POLICY_CUSTOM
depends on PM
endchoice
source "soc/arm/silabs_exx32/efr32bg22/Kconfig.defconfig.efr32bg22"
endif # SOC_SERIES_EFR32BG22

View file

@ -15,6 +15,7 @@ config SOC_SERIES_EFR32BG22
select HAS_SILABS_GECKO
select HAS_SWO
select SOC_FAMILY_EXX32
select SOC_GECKO_SERIES2
select SOC_GECKO_CMU
select SOC_GECKO_CORE
select SOC_GECKO_DEV_INIT

View file

@ -15,6 +15,7 @@ config SOC_SERIES_EFR32BG27
select HAS_SILABS_GECKO
select HAS_SWO
select SOC_FAMILY_EXX32
select SOC_GECKO_SERIES2
select SOC_GECKO_CMU
select SOC_GECKO_CORE
select SOC_GECKO_DEV_INIT

View file

@ -12,6 +12,7 @@ config SOC_SERIES_EFR32MG21
select CPU_HAS_FPU
select CPU_HAS_ARM_MPU
select SOC_FAMILY_EXX32
select SOC_GECKO_SERIES2
select HAS_SILABS_GECKO
select HAS_SWO
select SOC_GECKO_CMU

View file

@ -14,6 +14,7 @@ config SOC_SERIES_EFR32MG24
select ARMV8_M_DSP
select ARM_TRUSTZONE_M
select SOC_FAMILY_EXX32
select SOC_GECKO_SERIES2
select HAS_SILABS_GECKO
select HAS_SWO
select SOC_GECKO_CMU