soc/arm/silabs_exx32/common: support power management for EFR32BG
This commit introduces power management support for EFR32BG SoCs. Tested on the efr32bg_sltb010a board. Signed-off-by: Filip Kokosinski <fkokosinski@antmicro.com>
This commit is contained in:
parent
b0d785df18
commit
c939d6a473
3 changed files with 112 additions and 1 deletions
|
@ -27,6 +27,11 @@
|
|||
#include <sl_device_init_hfxo.h>
|
||||
#include <sl_device_init_nvic.h>
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#include <sl_hfxo_manager.h>
|
||||
#include <sl_power_manager.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
LOG_MODULE_REGISTER(soc, CONFIG_SOC_LOG_LEVEL);
|
||||
|
@ -205,6 +210,12 @@ static int silabs_exx32_init(const struct device *arg)
|
|||
sl_device_init_hfxo();
|
||||
sl_device_init_dpll();
|
||||
sl_device_init_emu();
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
sl_power_manager_init();
|
||||
sl_hfxo_manager_init();
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifdef CONFIG_SOC_GECKO_EMU_DCDC
|
||||
|
|
|
@ -1,15 +1,28 @@
|
|||
/*
|
||||
* 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 <em_emu.h>
|
||||
|
||||
#include <zephyr/logging/log.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
|
||||
|
@ -24,6 +37,52 @@ __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
|
||||
|
@ -55,6 +114,8 @@ __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 */
|
||||
|
@ -63,3 +124,37 @@ __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};
|
||||
|
||||
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
|
||||
|
|
|
@ -18,6 +18,11 @@ config NUM_IRQS
|
|||
config PM
|
||||
select COUNTER
|
||||
|
||||
choice PM_POLICY
|
||||
default PM_POLICY_CUSTOM
|
||||
depends on PM
|
||||
endchoice
|
||||
|
||||
source "soc/arm/silabs_exx32/efr32bg22/Kconfig.defconfig.efr32bg22"
|
||||
|
||||
endif # SOC_SERIES_EFR32BG22
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue