soc/arm/silabs: support BLE with PM in Series 2 SoCs
Using EM2 or deeper sleep states (where HF clocks are off) requires special care if BLE radio is used, since BLE radio relies on that clock, and its power/clock requirements need to be taken into account On SiLabs, radio PM is implemented as part of RAIL blob, which relies on sl_power_manager HAL service. I've implemented SoC PM state changes using sl_power_manager instead of emlib, and added call to RAIL PM initialization in Gecko HCI driver. Signed-off-by: Roman Dobrodii <rdobrodii@antmicro.com>
This commit is contained in:
parent
549358255f
commit
defb159ab1
9 changed files with 156 additions and 10 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <sl_hci_common_transport.h>
|
||||
#include <pa_conversions_efr32.h>
|
||||
#include <sl_bt_ll_zephyr.h>
|
||||
#include <rail.h>
|
||||
|
||||
#define LOG_LEVEL CONFIG_BT_HCI_DRIVER_LOG_LEVEL
|
||||
#include <zephyr/logging/log.h>
|
||||
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
1
samples/bluetooth/ibeacon/boards/efr32bg22_brd4184a.conf
Normal file
1
samples/bluetooth/ibeacon/boards/efr32bg22_brd4184a.conf
Normal file
|
@ -0,0 +1 @@
|
|||
CONFIG_PM=y
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(.)
|
||||
|
|
107
soc/arm/silabs_exx32/common/soc_power_pmgr.c
Normal file
107
soc/arm/silabs_exx32/common/soc_power_pmgr.c
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright (c) 2018, Piotr Mienkowski
|
||||
* Copyright (c) 2023, Antmicro <www.antmicro.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/pm/pm.h>
|
||||
#include <sl_power_manager.h>
|
||||
|
||||
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);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue