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); CMU_ClockSelectSet(cmuClock_EM01GRPBCLK, cmuSelect_HFRCODPLL);
#endif #endif
CMU_ClockSelectSet(cmuClock_EM23GRPACLK, cmuSelect_LFRCO); CMU_ClockSelectSet(cmuClock_EM23GRPACLK, cmuSelect_LFRCO);
CMU_ClockSelectSet(cmuClock_EM4GRPACLK, cmuSelect_LFRCO);
#if defined(RTCC_PRESENT) #if defined(RTCC_PRESENT)
CMU_ClockSelectSet(cmuClock_RTCC, cmuSelect_LFRCO); CMU_ClockSelectSet(cmuClock_RTCC, cmuSelect_LFRCO);
#endif #endif

View file

@ -6,12 +6,17 @@ CONFIG_BOARD_EFR32BG22_BRD4184A=y
CONFIG_CONSOLE=y CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y CONFIG_UART_CONSOLE=y
CONFIG_SERIAL=y CONFIG_SERIAL=y
CONFIG_CORTEX_M_SYSTICK=y
CONFIG_GPIO=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=y
CONFIG_SOC_GECKO_EMU_DCDC_MODE_ON=y CONFIG_SOC_GECKO_EMU_DCDC_MODE_ON=y
CONFIG_HW_STACK_PROTECTION=y CONFIG_HW_STACK_PROTECTION=y
CONFIG_CMU_HFCLK_HFRCO=y
CONFIG_PINCTRL=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_CONSOLE=y
CONFIG_UART_CONSOLE=y CONFIG_UART_CONSOLE=y
CONFIG_SERIAL=y CONFIG_SERIAL=y
CONFIG_CORTEX_M_SYSTICK=y
CONFIG_GPIO=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=y
CONFIG_SOC_GECKO_EMU_DCDC_MODE_ON=y CONFIG_SOC_GECKO_EMU_DCDC_MODE_ON=y
CONFIG_HW_STACK_PROTECTION=y CONFIG_HW_STACK_PROTECTION=y
CONFIG_CMU_HFCLK_HFRCO=y
CONFIG_PINCTRL=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 { &cpu0 {
clock-frequency = <76800000>; 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 { &usart0 {
@ -107,6 +111,10 @@
status = "okay"; status = "okay";
}; };
&burtc0 {
status = "okay";
};
&stimer0 { &stimer0 {
status = "okay"; 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_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_CORTEX_M_SYSTICK cortex_m_systick.c)
zephyr_library_sources_ifdef(CONFIG_ESP32C3_SYS_TIMER esp32c3_sys_timer.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_HPET_TIMER hpet.c)
zephyr_library_sources_ifdef(CONFIG_ITE_IT8XXX2_TIMER ite_it8xxx2_timer.c) zephyr_library_sources_ifdef(CONFIG_ITE_IT8XXX2_TIMER ite_it8xxx2_timer.c)
zephyr_library_sources_ifdef(CONFIG_LEON_GPTIMER leon_gptimer.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.cc13x2_cc26x2_rtc"
source "drivers/timer/Kconfig.cortex_m_systick" source "drivers/timer/Kconfig.cortex_m_systick"
source "drivers/timer/Kconfig.esp32c3_sys" source "drivers/timer/Kconfig.esp32c3_sys"
source "drivers/timer/Kconfig.gecko"
source "drivers/timer/Kconfig.hpet" source "drivers/timer/Kconfig.hpet"
source "drivers/timer/Kconfig.ite_it8xxx2" source "drivers/timer/Kconfig.ite_it8xxx2"
source "drivers/timer/Kconfig.leon_gptimer" 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>; interrupts = <15 0>, <16 0>;
}; };
&burtc0 {
interrupts = <18 0>;
};
&stimer0 { &stimer0 {
interrupts = <12 0>; interrupts = <12 0>;
}; };

View file

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

View file

@ -17,12 +17,56 @@
}; };
power-states { 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"; compatible = "zephyr,power-state";
power-state-name = "standby"; 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 { cpus {
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
@ -30,7 +74,19 @@
device_type = "cpu"; device_type = "cpu";
compatible = "arm,cortex-m33"; compatible = "arm,cortex-m33";
reg = <0>; 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"; status = "disabled";
}; };
burtc0: burtc@50064000 {
compatible = "silabs,gecko-burtc";
reg = <0x50064000 0x3034>;
status = "disabled";
};
stimer0: stimer@58000000 { stimer0: stimer@58000000 {
compatible = "silabs,gecko-stimer"; compatible = "silabs,gecko-stimer";
reg = <0x58000000 0x3054>; 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 that you should not set directly. The part number selection choice defines
the default value for this string. 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 config SOC_GECKO_CORE
bool bool
default y default y
@ -163,6 +175,12 @@ config SOC_GECKO_CMU
if 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 choice
prompt "High Frequency Clock Selection" prompt "High Frequency Clock Selection"
default CMU_HFCLK_HFXO default CMU_HFCLK_HFXO
@ -175,6 +193,7 @@ config CMU_HFCLK_HFXO
config CMU_HFCLK_LFXO config CMU_HFCLK_LFXO
bool "External low frequency crystal oscillator" bool "External low frequency crystal oscillator"
select CMU_NEED_LFXO
help help
Set this option to use the external low frequency crystal oscillator Set this option to use the external low frequency crystal oscillator
as high frequency clock. as high frequency clock.
@ -186,6 +205,37 @@ config CMU_HFCLK_HFRCO
endchoice 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 config CMU_HFXO_FREQ
int "External high frequency oscillator frequency" int "External high frequency oscillator frequency"
help help

View file

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

View file

@ -41,12 +41,44 @@ LOG_MODULE_REGISTER(soc, CONFIG_SOC_LOG_LEVEL);
* @brief Initialization parameters for the external high frequency oscillator * @brief Initialization parameters for the external high frequency oscillator
*/ */
static CMU_HFXOInit_TypeDef hfxoInit = CMU_HFXOINIT_DEFAULT; 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 * @brief Initialization parameters for the external low frequency oscillator
*/ */
static CMU_LFXOInit_TypeDef lfxoInit = CMU_LFXOINIT_DEFAULT; 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 * @brief Initialize the system clock
@ -85,29 +117,11 @@ static ALWAYS_INLINE void clock_init(void)
CMU_OscillatorEnable(cmuOsc_HFRCO, false, false); CMU_OscillatorEnable(cmuOsc_HFRCO, false, false);
#endif /* _SILICON_LABS_32B_SERIES_2 */ #endif /* _SILICON_LABS_32B_SERIES_2 */
#elif (defined CONFIG_CMU_HFCLK_LFXO) #elif (defined CONFIG_CMU_HFCLK_LFXO)
/* LFXO should've been already brought up by init_lfxo() */
#if defined(_SILICON_LABS_32B_SERIES_2) #if defined(_SILICON_LABS_32B_SERIES_2)
if (CMU_ClockSelectGet(cmuClock_SYSCLK) != cmuSelect_LFXO) { CMU_ClockSelectSet(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);
#else #else
if (CMU_ClockSelectGet(cmuClock_HF) != cmuSelect_LFXO) { CMU_ClockSelectSet(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); CMU_OscillatorEnable(cmuOsc_HFRCO, false, false);
#endif /* _SILICON_LABS_32B_SERIES_2 */ #endif /* _SILICON_LABS_32B_SERIES_2 */
#elif (defined CONFIG_CMU_HFCLK_HFRCO) #elif (defined CONFIG_CMU_HFCLK_HFRCO)
@ -204,6 +218,10 @@ static int silabs_exx32_init(void)
/* handle chip errata */ /* handle chip errata */
CHIP_Init(); CHIP_Init();
#ifdef CONFIG_CMU_NEED_LFXO
init_lfxo();
#endif
#ifdef CONFIG_SOC_GECKO_DEV_INIT #ifdef CONFIG_SOC_GECKO_DEV_INIT
sl_device_init_dcdc(); sl_device_init_dcdc();
sl_device_init_hfxo(); sl_device_init_hfxo();
@ -215,7 +233,7 @@ static int silabs_exx32_init(void)
sl_hfxo_manager_init(); sl_hfxo_manager_init();
#endif #endif
#else #else /* !CONFIG_SOC_GECKO_DEV_INIT */
#ifdef CONFIG_SOC_GECKO_EMU_DCDC #ifdef CONFIG_SOC_GECKO_EMU_DCDC
dcdc_init(); dcdc_init();
@ -234,7 +252,8 @@ static int silabs_exx32_init(void)
/* Configure SWO debug output */ /* Configure SWO debug output */
swo_init(); swo_init();
#endif #endif
#endif #endif /* !CONFIG_SOC_GECKO_DEV_INIT */
/* restore interrupt state */ /* restore interrupt state */
irq_unlock(oldLevel); irq_unlock(oldLevel);
return 0; return 0;

View file

@ -9,25 +9,14 @@
#include <zephyr/pm/pm.h> #include <zephyr/pm/pm.h>
#include <em_emu.h> #include <em_emu.h>
#include <zephyr/device.h>
#include <zephyr/drivers/counter.h>
LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL); 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: * Power state map:
* PM_STATE_RUNTIME_IDLE: EM1 Sleep * PM_STATE_RUNTIME_IDLE: EM1 Sleep
* PM_STATE_SUSPEND_TO_IDLE: EM2 Deep Sleep * PM_STATE_SUSPEND_TO_IDLE: EM2 Deep Sleep
* PM_STATE_STANDBY: EM3 Stop * PM_STATE_STANDBY: EM3 Stop
* PM_STATE_SOFT_OFF: EM4
*/ */
/* Invoke Low Power/System Off specific Tasks */ /* 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); 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 /* FIXME: When this function is entered the Kernel has disabled
* interrupts using BASEPRI register. This is incorrect as it prevents * interrupts using BASEPRI register. This is incorrect as it prevents
* waking up from any interrupt which priority is not 0. Work around the * 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: case PM_STATE_STANDBY:
EMU_EnterEM3(true); EMU_EnterEM3(true);
break; break;
case PM_STATE_SOFT_OFF:
EMU_EnterEM4();
break;
default: default:
LOG_DBG("Unsupported power state %u", state); LOG_DBG("Unsupported power state %u", state);
break; break;
@ -114,8 +60,6 @@ __weak void pm_state_set(enum pm_state state, uint8_t substate_id)
/* Clear PRIMASK */ /* Clear PRIMASK */
__enable_irq(); __enable_irq();
#endif
} }
/* Handle SOC specific activity after Low Power Mode Exit */ /* 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(state);
ARG_UNUSED(substate_id); 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 COUNTER
select UART_INTERRUPT_DRIVEN select UART_INTERRUPT_DRIVEN
choice PM_POLICY
default PM_POLICY_CUSTOM
depends on PM
endchoice
source "soc/arm/silabs_exx32/efr32bg22/Kconfig.defconfig.efr32bg22" source "soc/arm/silabs_exx32/efr32bg22/Kconfig.defconfig.efr32bg22"
endif # SOC_SERIES_EFR32BG22 endif # SOC_SERIES_EFR32BG22

View file

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

View file

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

View file

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

View file

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