Skip to content

Commit c939d6a

Browse files
fkokosinskicarlescufi
authored andcommitted
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>
1 parent b0d785d commit c939d6a

File tree

3 files changed

+112
-1
lines changed

3 files changed

+112
-1
lines changed

soc/arm/silabs_exx32/common/soc.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@
2727
#include <sl_device_init_hfxo.h>
2828
#include <sl_device_init_nvic.h>
2929

30+
#ifdef CONFIG_PM
31+
#include <sl_hfxo_manager.h>
32+
#include <sl_power_manager.h>
33+
#endif
34+
3035
#endif
3136

3237
LOG_MODULE_REGISTER(soc, CONFIG_SOC_LOG_LEVEL);
@@ -205,6 +210,12 @@ static int silabs_exx32_init(const struct device *arg)
205210
sl_device_init_hfxo();
206211
sl_device_init_dpll();
207212
sl_device_init_emu();
213+
214+
#ifdef CONFIG_PM
215+
sl_power_manager_init();
216+
sl_hfxo_manager_init();
217+
#endif
218+
208219
#else
209220

210221
#ifdef CONFIG_SOC_GECKO_EMU_DCDC

soc/arm/silabs_exx32/common/soc_power.c

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,28 @@
11
/*
22
* Copyright (c) 2018, Piotr Mienkowski
3+
* Copyright (c) 2023, Antmicro <www.antmicro.com>
34
*
45
* SPDX-License-Identifier: Apache-2.0
56
*/
67
#include <zephyr/kernel.h>
8+
#include <zephyr/logging/log.h>
79
#include <zephyr/pm/pm.h>
810
#include <em_emu.h>
911

10-
#include <zephyr/logging/log.h>
12+
#include <zephyr/device.h>
13+
#include <zephyr/drivers/counter.h>
14+
1115
LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL);
1216

17+
#ifdef CONFIG_SOC_GECKO_DEV_INIT
18+
#include <sl_power_manager.h>
19+
20+
static const struct device *const counter_dev = DEVICE_DT_GET(DT_NODELABEL(stimer0));
21+
static struct counter_alarm_cfg wakeup_cfg = {
22+
.callback = NULL
23+
};
24+
#endif
25+
1326
/*
1427
* Power state map:
1528
* PM_STATE_RUNTIME_IDLE: EM1 Sleep
@@ -24,6 +37,52 @@ __weak void pm_state_set(enum pm_state state, uint8_t substate_id)
2437

2538
LOG_DBG("SoC entering power state %d", state);
2639

40+
#ifdef CONFIG_SOC_GECKO_DEV_INIT
41+
sl_power_manager_em_t energy_mode;
42+
43+
/* Save RTCC IRQ priority */
44+
uint32_t rtcc_prio = NVIC_GetPriority(RTCC_IRQn);
45+
/*
46+
* When this function is entered the Kernel has disabled handling interrupts
47+
* with priority other than 0. The Interrupt for the timer used to wake up
48+
* the cpu has priority equal to 1. Manually set this priority to 0 so that
49+
* cpu could exit sleep state.
50+
*
51+
* Note on priority value: set priority to -1 because z_arm_irq_priority_set
52+
* offsets the priorities by 1.
53+
* This function call will effectively set the priority to 0.
54+
*/
55+
z_arm_irq_priority_set(RTCC_IRQn, -1, 0);
56+
57+
switch (state) {
58+
59+
case PM_STATE_STANDBY:
60+
energy_mode = SL_POWER_MANAGER_EM3;
61+
62+
/* Limit sleep level to given state */
63+
sl_power_manager_add_em_requirement(energy_mode);
64+
65+
counter_start(counter_dev);
66+
counter_set_channel_alarm(counter_dev, 0, &wakeup_cfg);
67+
sl_power_manager_sleep();
68+
k_cpu_idle();
69+
counter_stop(counter_dev);
70+
71+
/* Remove sleep level limit */
72+
sl_power_manager_remove_em_requirement(energy_mode);
73+
break;
74+
75+
default:
76+
LOG_DBG("Unsupported power state %u", state);
77+
break;
78+
}
79+
80+
LOG_DBG("SoC leaving power state %d", state);
81+
82+
/* Restore RTCC IRQ priority */
83+
z_arm_irq_priority_set(RTCC_IRQn, (rtcc_prio - 1), 0);
84+
#else
85+
2786
/* FIXME: When this function is entered the Kernel has disabled
2887
* interrupts using BASEPRI register. This is incorrect as it prevents
2988
* 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)
55114

56115
/* Clear PRIMASK */
57116
__enable_irq();
117+
118+
#endif
58119
}
59120

60121
/* 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)
63124
ARG_UNUSED(state);
64125
ARG_UNUSED(substate_id);
65126
}
127+
128+
#if defined(CONFIG_SOC_GECKO_DEV_INIT) && defined(CONFIG_PM_POLICY_CUSTOM)
129+
/* CONFIG_PM_POLICY_CUSTOM allows us to set the next alarm to a specific number
130+
* of ticks in the future. This is needed for the Gecko SleepTimer to wake up
131+
* the device properly.
132+
*/
133+
static const struct pm_state_info pm_min_residency[] =
134+
PM_STATE_INFO_LIST_FROM_DT_CPU(DT_NODELABEL(cpu0));
135+
struct pm_state_info pm_state_active = {PM_STATE_ACTIVE, 0, 0, 0};
136+
137+
struct pm_state_info *pm_policy_next_state(uint8_t cpu, int32_t ticks)
138+
{
139+
int i;
140+
141+
for (i = ARRAY_SIZE(pm_min_residency) - 1; i >= 0; i--) {
142+
143+
if ((ticks == K_TICKS_FOREVER) ||
144+
(ticks >= k_us_to_ticks_ceil32(
145+
pm_min_residency[i].min_residency_us))) {
146+
LOG_DBG("Selected power state %d "
147+
"(ticks: %d, min_residency: %u)",
148+
pm_min_residency[i].state, ticks,
149+
pm_min_residency[i].min_residency_us);
150+
151+
/* Busy waiting for 1 tick to flush UART buffer */
152+
k_busy_wait(k_ticks_to_us_floor32(1));
153+
wakeup_cfg.ticks = ticks;
154+
155+
return ((struct pm_state_info *) &pm_min_residency[i]);
156+
}
157+
}
158+
return (struct pm_state_info *)(&pm_state_active);
159+
}
160+
#endif

soc/arm/silabs_exx32/efr32bg22/Kconfig.defconfig.series

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ config NUM_IRQS
1818
config PM
1919
select COUNTER
2020

21+
choice PM_POLICY
22+
default PM_POLICY_CUSTOM
23+
depends on PM
24+
endchoice
25+
2126
source "soc/arm/silabs_exx32/efr32bg22/Kconfig.defconfig.efr32bg22"
2227

2328
endif # SOC_SERIES_EFR32BG22

0 commit comments

Comments
 (0)