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:
parent
bdb8024df3
commit
cb14d8b099
21 changed files with 491 additions and 136 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
33
drivers/timer/Kconfig.gecko
Normal file
33
drivers/timer/Kconfig.gecko
Normal 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'.
|
237
drivers/timer/gecko_burtc_timer.c
Normal file
237
drivers/timer/gecko_burtc_timer.c
Normal 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);
|
|
@ -73,6 +73,10 @@
|
|||
interrupts = <15 0>, <16 0>;
|
||||
};
|
||||
|
||||
&burtc0 {
|
||||
interrupts = <18 0>;
|
||||
};
|
||||
|
||||
&stimer0 {
|
||||
interrupts = <12 0>;
|
||||
};
|
||||
|
|
|
@ -74,6 +74,10 @@
|
|||
interrupts = <18 0>, <19 0>;
|
||||
};
|
||||
|
||||
&burtc0 {
|
||||
interrupts = <23 0>;
|
||||
};
|
||||
|
||||
&stimer0 {
|
||||
interrupts = <15 0>;
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
|
|
15
dts/bindings/timer/silabs,gecko-burtc.yaml
Normal file
15
dts/bindings/timer/silabs,gecko-burtc.yaml
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue