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

@ -50,7 +50,6 @@ static int thunderboard_init_clocks(void)
CMU_ClockSelectSet(cmuClock_EM01GRPBCLK, cmuSelect_HFRCODPLL);
#endif
CMU_ClockSelectSet(cmuClock_EM23GRPACLK, cmuSelect_LFRCO);
CMU_ClockSelectSet(cmuClock_EM4GRPACLK, cmuSelect_LFRCO);
#if defined(RTCC_PRESENT)
CMU_ClockSelectSet(cmuClock_RTCC, cmuSelect_LFRCO);
#endif

View file

@ -6,12 +6,17 @@ CONFIG_BOARD_EFR32BG22_BRD4184A=y
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y
CONFIG_SERIAL=y
CONFIG_CORTEX_M_SYSTICK=y
CONFIG_GPIO=y
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=76800000
CONFIG_CMU_HFCLK_HFXO=y
CONFIG_SOC_GECKO_EMU_DCDC=y
CONFIG_SOC_GECKO_EMU_DCDC_MODE_ON=y
CONFIG_HW_STACK_PROTECTION=y
CONFIG_CMU_HFCLK_HFRCO=y
CONFIG_PINCTRL=y
# Used if SysTick is enabled, ignored for BURTC
# (BURTC uses TIMER_READS_ITS_FREQUENCY_AT_RUNTIME)
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=76800000
# Use BURTC as system clock source
CONFIG_GECKO_BURTC_TIMER=y
CONFIG_CMU_BURTCCLK_LFXO=y
CONFIG_SYS_CLOCK_TICKS_PER_SEC=1024

View file

@ -6,12 +6,17 @@ CONFIG_BOARD_EFR32BG27_BRD2602A=y
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y
CONFIG_SERIAL=y
CONFIG_CORTEX_M_SYSTICK=y
CONFIG_GPIO=y
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=76800000
CONFIG_CMU_HFCLK_HFXO=y
CONFIG_SOC_GECKO_EMU_DCDC=y
CONFIG_SOC_GECKO_EMU_DCDC_MODE_ON=y
CONFIG_HW_STACK_PROTECTION=y
CONFIG_CMU_HFCLK_HFRCO=y
CONFIG_PINCTRL=y
# Used if SysTick is enabled, ignored for BURTC
# (BURTC uses TIMER_READS_ITS_FREQUENCY_AT_RUNTIME)
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=76800000
# Use BURTC as system clock source
CONFIG_GECKO_BURTC_TIMER=y
CONFIG_CMU_BURTCCLK_LFXO=y
CONFIG_SYS_CLOCK_TICKS_PER_SEC=1024

View file

@ -47,6 +47,10 @@
&cpu0 {
clock-frequency = <76800000>;
/* Enable EM1,2. This means BURTC has to be used as sys_clock
* or the system won't wake up
*/
cpu-power-states = <&pstate_em1 &pstate_em2>;
};
&usart0 {
@ -107,6 +111,10 @@
status = "okay";
};
&burtc0 {
status = "okay";
};
&stimer0 {
status = "okay";
};

View file

@ -11,6 +11,7 @@ zephyr_library_sources_ifdef(CONFIG_INTEL_ADSP_TIMER intel_adsp_timer.c)
zephyr_library_sources_ifdef(CONFIG_CC13X2_CC26X2_RTC_TIMER cc13x2_cc26x2_rtc_timer.c)
zephyr_library_sources_ifdef(CONFIG_CORTEX_M_SYSTICK cortex_m_systick.c)
zephyr_library_sources_ifdef(CONFIG_ESP32C3_SYS_TIMER esp32c3_sys_timer.c)
zephyr_library_sources_ifdef(CONFIG_GECKO_BURTC_TIMER gecko_burtc_timer.c)
zephyr_library_sources_ifdef(CONFIG_HPET_TIMER hpet.c)
zephyr_library_sources_ifdef(CONFIG_ITE_IT8XXX2_TIMER ite_it8xxx2_timer.c)
zephyr_library_sources_ifdef(CONFIG_LEON_GPTIMER leon_gptimer.c)

View file

@ -71,6 +71,7 @@ source "drivers/timer/Kconfig.cavs"
source "drivers/timer/Kconfig.cc13x2_cc26x2_rtc"
source "drivers/timer/Kconfig.cortex_m_systick"
source "drivers/timer/Kconfig.esp32c3_sys"
source "drivers/timer/Kconfig.gecko"
source "drivers/timer/Kconfig.hpet"
source "drivers/timer/Kconfig.ite_it8xxx2"
source "drivers/timer/Kconfig.leon_gptimer"

View file

@ -0,0 +1,33 @@
# Copyright (c) 2023 Antmicro <www.antmicro.com>
# SPDX-License-Identifier: Apache-2.0
config GECKO_BURTC_TIMER
bool "SiLabs Gecko BURTC system clock driver"
depends on SOC_GECKO_SERIES2
depends on DT_HAS_SILABS_GECKO_BURTC_ENABLED
select SOC_GECKO_BURTC
select TICKLESS_CAPABLE
select TIMER_READS_ITS_FREQUENCY_AT_RUNTIME
help
If you enable this, BURTC will be used to provide hw_cycles and
kernel ticks instead of Cortex-M SysTick. You need this for system
to be able to keep track of time and wake up from EM2 & EM3 sleep
states.
NOTE:
Using BURTC instead of SysTick has a large impact on kernel timing
precision.
1. You won't be able to use the usual 0.1ms-granularity tickless
scheduling. Kernel tick duration should be at least 6 BURTC clock
cycles, that is ~183 us @ 32768 Hz (LFXO, LFRCO) or
~6 ms @ 1000 Hz (ULFRCO).
2. In general, accuracy of real-time scheduling by kernel will be
degraded: all timeout-based facilities, such as timers, delayable
work, k_sleep, will issue thread wake ups less precisely than when
using SysTick timer.
3. hw_cycles granularity will be equal to 1 BURTC clock, that is
~31 us @ 32768 Hz or ~1 ms @ 1000 Hz. This reduces timing
precision of all code which relies on cycles API, e.g.
k_cycle_get_32() and similar functions.
If unsure, say 'N'.

View file

@ -0,0 +1,237 @@
/*
* Copyright (c) 2023 Antmicro <www.antmicro.com>
*
* Based on:
* sam0_rtc_timer.c Copyright (c) 2018 omSquare s.r.o.
* intel_adsp_timer.c Copyright (c) 2020 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT silabs_gecko_burtc
/**
* @file
* @brief SiLabs Gecko BURTC-based sys_clock driver
*
*/
#include <zephyr/device.h>
#include <soc.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/timer/system_timer.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/sys_clock.h>
#include <zephyr/irq.h>
#include <zephyr/spinlock.h>
#include <zephyr/logging/log.h>
#include "em_device.h"
#include "em_cmu.h"
#include "em_burtc.h"
LOG_MODULE_REGISTER(gecko_burtc_timer);
/* Maximum time interval between timer interrupts (in hw_cycles) */
#define MAX_TIMEOUT_CYC (UINT32_MAX >> 1)
/*
* Mininum time interval between now and IRQ firing that can be scheduled.
* The main cause for this is LFSYNC register update, which requires several
* LF clk cycles for synchronization.
* Seee e.g. "4.2.4.4.4 LFSYNC Registers" in "EFR32xG22 Reference Manual"
*/
#define MIN_DELAY_CYC (6u)
#define TIMER_IRQ (DT_INST_IRQN(0))
#if defined(CONFIG_TEST)
/* See tests/kernel/context */
const int32_t z_sys_timer_irq_for_test = TIMER_IRQ;
#endif
/* With CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME, that's where we
* should write hw_cycles timer clock frequency upon init
*/
extern int z_clock_hw_cycles_per_sec;
/* Number of hw_cycles clocks per 1 kernel tick */
static uint32_t g_cyc_per_tick;
/* MAX_TIMEOUT_CYC expressed as ticks */
static uint32_t g_max_timeout_ticks;
/* Value of BURTC counter when the previous kernel tick was announced */
static atomic_t g_last_count;
/* Spinlock to sync between Compare ISR and update of Compare register */
static struct k_spinlock g_lock;
static void burtc_isr(const void *arg)
{
ARG_UNUSED(arg);
/* Clear the interrupt */
BURTC_IntClear(BURTC_IF_COMP);
uint32_t curr = BURTC_CounterGet();
/* NOTE: this is the only place where g_last_count is modified,
* so we don't need to do make the whole read-and-modify atomic, just
* writing it behind the memory barrier is enough
*/
uint32_t prev = atomic_get(&g_last_count);
/* How many ticks have we not announced since the last announcement */
uint32_t unannounced = (curr - prev) / g_cyc_per_tick;
atomic_set(&g_last_count, prev + unannounced * g_cyc_per_tick);
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
/* Counter value on which announcement should be made */
uint32_t next = prev + g_cyc_per_tick;
/* `next` can be too close in the future since we're trying to
* announce the very next tick - in that case we skip one and
* announce the one after it instead
*/
if ((next - curr) < MIN_DELAY_CYC) {
next += g_cyc_per_tick;
}
BURTC_CompareSet(0, next);
}
sys_clock_announce(unannounced);
}
void sys_clock_set_timeout(int32_t ticks, bool idle)
{
ARG_UNUSED(idle);
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
return;
}
/*
* calculate 'ticks' value that specifies which tick to announce,
* beginning from the closest upcoming one:
* 0 - announce upcoming tick itself
* 1 - skip upcoming one, but announce the one after it, etc.
*/
ticks = (ticks == K_TICKS_FOREVER) ? g_max_timeout_ticks : ticks;
ticks = CLAMP(ticks - 1, 0, g_max_timeout_ticks);
k_spinlock_key_t key = k_spin_lock(&g_lock);
uint32_t curr = BURTC_CounterGet();
uint32_t prev = atomic_get(&g_last_count);
/* How many ticks have we not announced since the last announcement */
uint32_t unannounced = (curr - prev) / g_cyc_per_tick;
/* Which tick to announce (counting from the last announced one) */
uint32_t to_announce = unannounced + ticks + 1;
/* Force maximum interval between announcements. If we sit without
* announcements for too long, counter will roll over and we'll lose
* track of unannounced ticks.
*/
to_announce = MIN(to_announce, g_max_timeout_ticks);
/* Counter value on which announcement should be made */
uint32_t next = prev + to_announce * g_cyc_per_tick;
/* `next` can be too close in the future if we're trying to announce
* the very next tick - in that case we skip one and announce the one
* after it instead
*/
if ((next - curr) < MIN_DELAY_CYC) {
next += g_cyc_per_tick;
}
BURTC_CompareSet(0, next);
k_spin_unlock(&g_lock, key);
}
uint32_t sys_clock_elapsed(void)
{
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
return 0;
} else {
return (BURTC_CounterGet() - g_last_count) / g_cyc_per_tick;
}
}
uint32_t sys_clock_cycle_get_32(void)
{
/* API note: this function is unrelated to kernel ticks, it returns
* a value of some 32-bit hw_cycles counter which counts with
* z_clock_hw_cycles_per_sec frequency
*/
return BURTC_CounterGet();
}
static int burtc_init(void)
{
uint32_t hw_clock_freq;
BURTC_Init_TypeDef init = BURTC_INIT_DEFAULT;
/* Enable clock for BURTC CSRs on APB */
CMU_ClockEnable(cmuClock_BURTC, true);
/* Configure BURTC LF clocksource according to Kconfig */
#if defined(CONFIG_CMU_BURTCCLK_LFXO)
CMU_ClockSelectSet(cmuClock_BURTC, cmuSelect_LFXO);
#elif defined(CONFIG_CMU_BURTCCLK_LFRCO)
CMU_ClockSelectSet(cmuClock_BURTC, cmuSelect_LFRCO);
#elif defined(CONFIG_CMU_BURTCCLK_ULFRCO)
CMU_ClockSelectSet(cmuClock_BURTC, cmuSelect_ULFRCO);
#else
#error "Unsupported BURTC clock specified"
#endif
/* Calculate timing constants and init BURTC */
hw_clock_freq = CMU_ClockFreqGet(cmuClock_BURTC);
z_clock_hw_cycles_per_sec = hw_clock_freq;
BUILD_ASSERT(CONFIG_SYS_CLOCK_TICKS_PER_SEC > 0,
"Invalid CONFIG_SYS_CLOCK_TICKS_PER_SEC value");
g_cyc_per_tick = hw_clock_freq / CONFIG_SYS_CLOCK_TICKS_PER_SEC;
__ASSERT(g_cyc_per_tick >= MIN_DELAY_CYC,
"%u cycle-long tick is too short to be scheduled "
"(min is %u). Config: SYS_CLOCK_TICKS_PER_SEC is "
"%d and timer frequency is %u",
g_cyc_per_tick, MIN_DELAY_CYC, CONFIG_SYS_CLOCK_TICKS_PER_SEC,
hw_clock_freq);
g_max_timeout_ticks = MAX_TIMEOUT_CYC / g_cyc_per_tick;
init.clkDiv = 1;
init.start = false;
BURTC_Init(&init);
/* Enable compare match interrupt */
BURTC_IntClear(BURTC_IF_COMP);
BURTC_IntEnable(BURTC_IF_COMP);
NVIC_ClearPendingIRQ(TIMER_IRQ);
IRQ_CONNECT(TIMER_IRQ, DT_INST_IRQ(0, priority), burtc_isr, 0, 0);
irq_enable(TIMER_IRQ);
/* Start the timer and announce 1 kernel tick */
atomic_set(&g_last_count, 0);
BURTC_CompareSet(0, g_cyc_per_tick);
BURTC_SyncWait();
BURTC->CNT = 0;
BURTC_Start();
return 0;
}
SYS_INIT(burtc_init, PRE_KERNEL_2,
CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);

View file

@ -73,6 +73,10 @@
interrupts = <15 0>, <16 0>;
};
&burtc0 {
interrupts = <18 0>;
};
&stimer0 {
interrupts = <12 0>;
};

View file

@ -74,6 +74,10 @@
interrupts = <18 0>, <19 0>;
};
&burtc0 {
interrupts = <23 0>;
};
&stimer0 {
interrupts = <15 0>;
};

View file

@ -17,12 +17,56 @@
};
power-states {
standby: standby {
/*
* EM1 is a basic "CPU WFI idle", all high-freq clocks remain
* enabled.
*/
pstate_em1: em1 {
compatible = "zephyr,power-state";
power-state-name = "runtime-idle";
min-residency-us = <4>;
/* HFXO remains active */
exit-latency-us = <2>;
};
/*
* EM2 is a deepsleep with HF clocks disabled by HW, voltages
* scaled down, etc.
*/
pstate_em2: em2 {
compatible = "zephyr,power-state";
power-state-name = "suspend-to-idle";
min-residency-us = <260>;
exit-latency-us = <250>;
};
/*
* EM3 seems to be exactly the same as EM2 except that
* LFXO & LFRCO should be disabled, so you must use ULFRCO
* as BURTC clock for the system to not lose track of time and
* wake up.
*/
pstate_em3: em3 {
compatible = "zephyr,power-state";
power-state-name = "standby";
min-residency-us = <50000>;
min-residency-us = <20000>;
exit-latency-us = <2000>;
};
/*
* EM4 does not preserve CPU or RAM state, so system runs
* through a cold boot upon wake up. BURTC can wake up the
* system from EM4 but that has to be manually configured
* by the application in BURTC registers.
*/
pstate_em4: em4 {
compatible = "zephyr,power-state";
power-state-name = "soft-off";
min-residency-us = <100000>;
exit-latency-us = <80000>;
};
};
cpus {
#address-cells = <1>;
#size-cells = <0>;
@ -30,7 +74,19 @@
device_type = "cpu";
compatible = "arm,cortex-m33";
reg = <0>;
cpu-power-states = <&standby>;
/*
* EM1 is enabled by default because it is
* unconditionally safe.
*
* EM2/3 can be enabled by the board code if proper
* timing configuration is ensured:
* - for EM2, EM3: BURTC used as sys_clock
* - for EM3: BURTC clocked from ULFRCO
* Using BURTC as sys_clock instead of SysTick
* has implications on system performance. Read
* KConfig documentation entry before enabling it.
*/
cpu-power-states = <&pstate_em1>;
};
};
@ -67,6 +123,12 @@
status = "disabled";
};
burtc0: burtc@50064000 {
compatible = "silabs,gecko-burtc";
reg = <0x50064000 0x3034>;
status = "disabled";
};
stimer0: stimer@58000000 {
compatible = "silabs,gecko-stimer";
reg = <0x58000000 0x3054>;

View file

@ -0,0 +1,15 @@
# Copyright (c) 2023 Antmicro <www.antmicro.com>
# SPDX-License-Identifier: Apache-2.0
description: SiLabs Gecko BURTC timer
compatible: "silabs,gecko-burtc"
include: base.yaml
properties:
reg:
required: true
interrupts:
required: true

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_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