diff --git a/boards/arm/efr32_thunderboard/Kconfig.defconfig b/boards/arm/efr32_thunderboard/Kconfig.defconfig index 1fe8373bfac..df366db88b1 100644 --- a/boards/arm/efr32_thunderboard/Kconfig.defconfig +++ b/boards/arm/efr32_thunderboard/Kconfig.defconfig @@ -32,6 +32,7 @@ config COMMON_LIBC_MALLOC_ARENA_SIZE default 8192 config MAIN_STACK_SIZE + default 3072 if PM default 2304 choice BT_HCI_BUS_TYPE diff --git a/drivers/bluetooth/hci/Kconfig b/drivers/bluetooth/hci/Kconfig index 8fee41d9b27..93b9ab5a598 100644 --- a/drivers/bluetooth/hci/Kconfig +++ b/drivers/bluetooth/hci/Kconfig @@ -59,6 +59,7 @@ config BT_STM32_IPM config BT_SILABS_HCI bool "Silicon Labs Bluetooth interface" depends on SOC_SERIES_EFR32BG22 || SOC_SERIES_EFR32MG24 + depends on !PM || SOC_GECKO_PM_BACKEND_PMGR select ENTROPY_GENERATOR select MBEDTLS select MBEDTLS_PSA_CRYPTO_C diff --git a/drivers/bluetooth/hci/slz_hci.c b/drivers/bluetooth/hci/slz_hci.c index 78b944029e7..16da3e2ca71 100644 --- a/drivers/bluetooth/hci/slz_hci.c +++ b/drivers/bluetooth/hci/slz_hci.c @@ -10,6 +10,7 @@ #include #include #include +#include #define LOG_LEVEL CONFIG_BT_HCI_DRIVER_LOG_LEVEL #include @@ -156,6 +157,19 @@ static int slz_bt_open(void) sl_bthci_init_upper(); +#ifdef CONFIG_PM + { + RAIL_Status_t status = RAIL_InitPowerManager(); + + if (status != RAIL_STATUS_NO_ERROR) { + LOG_ERR("RAIL: failed to initialize power management, status=%d", + status); + ret = -EIO; + goto deinit; + } + } +#endif + LOG_DBG("SiLabs BT HCI started"); return 0; diff --git a/drivers/timer/gecko_burtc_timer.c b/drivers/timer/gecko_burtc_timer.c index babbae41bd4..e3d077d4208 100644 --- a/drivers/timer/gecko_burtc_timer.c +++ b/drivers/timer/gecko_burtc_timer.c @@ -70,7 +70,7 @@ static atomic_t g_last_count; static struct k_spinlock g_lock; /* Set to true when timer is initialized */ -static atomic_t g_init = ATOMIC_INIT(0); +static bool g_init; static void burtc_isr(const void *arg) { @@ -161,12 +161,10 @@ void sys_clock_set_timeout(int32_t ticks, bool idle) uint32_t sys_clock_elapsed(void) { - if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { + if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL) || !g_init) { return 0; - } else if (atomic_get(&g_init)) { - return (BURTC_CounterGet() - g_last_count) / g_cyc_per_tick; } else { - return 0; + return (BURTC_CounterGet() - g_last_count) / g_cyc_per_tick; } } @@ -176,10 +174,10 @@ uint32_t sys_clock_cycle_get_32(void) * a value of some 32-bit hw_cycles counter which counts with * z_clock_hw_cycles_per_sec frequency */ - if (atomic_get(&g_init)) { - return BURTC_CounterGet(); - } else { + if (!g_init) { return 0; + } else { + return BURTC_CounterGet(); } } @@ -222,7 +220,7 @@ static int burtc_init(void) init.clkDiv = 1; init.start = false; BURTC_Init(&init); - atomic_set(&g_init, 1); + g_init = true; /* Enable compare match interrupt */ BURTC_IntClear(BURTC_IF_COMP); diff --git a/samples/bluetooth/ibeacon/boards/efr32bg22_brd4184a.conf b/samples/bluetooth/ibeacon/boards/efr32bg22_brd4184a.conf new file mode 100644 index 00000000000..2df782efc71 --- /dev/null +++ b/samples/bluetooth/ibeacon/boards/efr32bg22_brd4184a.conf @@ -0,0 +1 @@ +CONFIG_PM=y diff --git a/soc/arm/silabs_exx32/Kconfig b/soc/arm/silabs_exx32/Kconfig index 8ef33105092..e7759151981 100644 --- a/soc/arm/silabs_exx32/Kconfig +++ b/soc/arm/silabs_exx32/Kconfig @@ -126,6 +126,22 @@ config SOC_GECKO_TRNG help Set if the SoC has a True Random Number Generator (TRNG) module. +if PM + +config SOC_GECKO_PM_BACKEND_PMGR + bool + default y if SOC_GECKO_SERIES2 + help + Implement PM using sl_power_manager service from Gecko SDK + +config SOC_GECKO_PM_BACKEND_EMU + bool + default y if !SOC_GECKO_PM_BACKEND_PMGR + help + Implement PM using direct calls to EMU driver in emlib + +endif + config SOC_GECKO_EMU_DCDC bool "SoC DC/DC regulator" select SOC_GECKO_EMU diff --git a/soc/arm/silabs_exx32/Kconfig.defconfig b/soc/arm/silabs_exx32/Kconfig.defconfig index ab6c9e102ae..8f582653b3c 100644 --- a/soc/arm/silabs_exx32/Kconfig.defconfig +++ b/soc/arm/silabs_exx32/Kconfig.defconfig @@ -12,4 +12,10 @@ config SOC_GECKO_EMU config CORTEX_M_SYSTICK default n if GECKO_BURTC_TIMER +# With sl_power_manager, pm_state_set()'s stack footrpting is noticeably +# large, especially with logs enabled. Since it is called from IDLE task, +# its stack size has to be increased +config IDLE_STACK_SIZE + default 512 if SOC_GECKO_PM_BACKEND_PMGR + endif diff --git a/soc/arm/silabs_exx32/common/CMakeLists.txt b/soc/arm/silabs_exx32/common/CMakeLists.txt index 34e7015db95..ac965e28d46 100644 --- a/soc/arm/silabs_exx32/common/CMakeLists.txt +++ b/soc/arm/silabs_exx32/common/CMakeLists.txt @@ -2,5 +2,7 @@ zephyr_sources(soc.c) -zephyr_sources_ifdef(CONFIG_PM soc_power.c) +zephyr_sources_ifdef(CONFIG_SOC_GECKO_PM_BACKEND_EMU soc_power.c) +zephyr_sources_ifdef(CONFIG_SOC_GECKO_PM_BACKEND_PMGR soc_power_pmgr.c) + zephyr_include_directories(.) diff --git a/soc/arm/silabs_exx32/common/soc_power_pmgr.c b/soc/arm/silabs_exx32/common/soc_power_pmgr.c new file mode 100644 index 00000000000..aa303874dde --- /dev/null +++ b/soc/arm/silabs_exx32/common/soc_power_pmgr.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2018, Piotr Mienkowski + * Copyright (c) 2023, Antmicro + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include + +LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL); + +/* + * 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 */ +__weak void pm_state_set(enum pm_state state, uint8_t substate_id) +{ + ARG_UNUSED(substate_id); + sl_power_manager_em_t energy_mode = SL_POWER_MANAGER_EM0; + + LOG_DBG("SoC entering power state %d", state); + + switch (state) { + case PM_STATE_RUNTIME_IDLE: + energy_mode = SL_POWER_MANAGER_EM1; + break; + case PM_STATE_SUSPEND_TO_IDLE: + energy_mode = SL_POWER_MANAGER_EM2; + break; + case PM_STATE_STANDBY: + energy_mode = SL_POWER_MANAGER_EM3; + break; + case PM_STATE_SOFT_OFF: + energy_mode = SL_POWER_MANAGER_EM4; + break; + default: + LOG_DBG("Unsupported power state %d", state); + break; + } + + /* 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 + * issue and disable interrupts using PRIMASK register as recommended + * by ARM. + */ + + /* Set PRIMASK */ + __disable_irq(); + /* Set BASEPRI to 0 */ + irq_unlock(0); + + LOG_DBG("Entry to energy mode %d", energy_mode); + + if (energy_mode != SL_POWER_MANAGER_EM0) { + sl_power_manager_add_em_requirement(energy_mode); + sl_power_manager_sleep(); + k_cpu_idle(); + sl_power_manager_remove_em_requirement(energy_mode); + } + + LOG_DBG("Exit from energy mode %d", energy_mode); + + /* Clear PRIMASK */ + __enable_irq(); +} + + +/** + * Some SiLabs blobs, such as RAIL, call directly into sl_power_manager, and + * for that they had to include sl_power_manager.h during build. Some of those + * blobs have been compiled with -DSL_POWER_MANAGER_DEBUG=1, making inlined + * functions from that header to rely on + * sli_power_manager_debug_log_em_requirement() callback. + * + * This is irrespective of whether *we* enable SL_POWER_MANAGER_DEBUG when + * compiling sl_power_manager code as part of Zephyr build. + * + * Therefore, we provide sli_power_manager_debug_log_em_requirement() + * definition here just to satisfy those blobs. It will also be used if we + * attempt to build sl_power_manager with SL_POWER_MANAGER_DEBUG enabled. + * + * @note Please keep this at the end of the file. + */ + +#ifdef sli_power_manager_debug_log_em_requirement +#undef sli_power_manager_debug_log_em_requirement +#endif + +void sli_power_manager_debug_init(void) +{ +} + +void sli_power_manager_debug_log_em_requirement( + sl_power_manager_em_t em, bool add, const char *name) +{ + LOG_DBG("Set PM requirement em=%d add=%s name=%s", + (int)em, add ? "true" : "false", name); +}