blob: ad7ae18d9bb191ee80dac48e53a51c0d09b20d38 [file] [log] [blame]
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/sys/poweroff.h>
#include <zephyr/toolchain.h>
#include <zephyr/pm/policy.h>
#include <zephyr/arch/common/pm_s2ram.h>
#include <hal/nrf_resetinfo.h>
#include <hal/nrf_lrcconf.h>
#include <hal/nrf_memconf.h>
#include <zephyr/cache.h>
#include <power.h>
#include "pm_s2ram.h"
static void suspend_common(void)
{
/* Flush, disable and power down DCACHE */
sys_cache_data_flush_all();
sys_cache_data_disable();
nrf_memconf_ramblock_control_enable_set(NRF_MEMCONF, RAMBLOCK_POWER_ID,
RAMBLOCK_CONTROL_BIT_DCACHE, false);
if (IS_ENABLED(CONFIG_ICACHE)) {
/* Disable and power down ICACHE */
sys_cache_instr_disable();
nrf_memconf_ramblock_control_enable_set(NRF_MEMCONF, RAMBLOCK_POWER_ID,
RAMBLOCK_CONTROL_BIT_ICACHE, false);
}
/* Disable retention */
nrf_lrcconf_retain_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_DOMAIN_0, false);
nrf_lrcconf_poweron_force_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_DOMAIN_0, false);
}
void nrf_poweroff(void)
{
nrf_resetinfo_resetreas_local_set(NRF_RESETINFO, 0);
nrf_resetinfo_restore_valid_set(NRF_RESETINFO, false);
nrf_lrcconf_retain_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_MAIN, false);
/* TODO: Move it around k_cpu_idle() implementation. */
nrf_lrcconf_poweron_force_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_MAIN, false);
nrf_lrcconf_poweron_force_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_DOMAIN_0, false);
suspend_common();
nrf_lrcconf_task_trigger(NRF_LRCCONF010, NRF_LRCCONF_TASK_SYSTEMOFFREADY);
__set_BASEPRI(0);
__ISB();
__DSB();
__WFI();
CODE_UNREACHABLE;
}
#if IS_ENABLED(CONFIG_PM_S2RAM)
/* Resume domain after local suspend to RAM. */
static void sys_resume(void)
{
if (IS_ENABLED(CONFIG_ICACHE)) {
/* Power up and re-enable ICACHE */
nrf_memconf_ramblock_control_enable_set(NRF_MEMCONF, RAMBLOCK_POWER_ID,
RAMBLOCK_CONTROL_BIT_ICACHE, true);
sys_cache_instr_enable();
}
if (IS_ENABLED(CONFIG_DCACHE)) {
/* Power up and re-enable DCACHE */
nrf_memconf_ramblock_control_enable_set(NRF_MEMCONF, RAMBLOCK_POWER_ID,
RAMBLOCK_CONTROL_BIT_DCACHE, true);
sys_cache_data_enable();
}
/* Re-enable domain retention. */
nrf_lrcconf_retain_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_DOMAIN_0, true);
/* TODO: Move it around k_cpu_idle() implementation. */
nrf_lrcconf_poweron_force_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_MAIN,
!IS_ENABLED(CONFIG_SOC_NRF54H20_CPURAD));
}
/* Function called during local domain suspend to RAM. */
static int sys_suspend_to_ram(void)
{
/* Set intormation which is used on domain wakeup to determine if resume from RAM shall
* be performed.
*/
nrf_resetinfo_resetreas_local_set(NRF_RESETINFO,
NRF_RESETINFO_RESETREAS_LOCAL_UNRETAINED_MASK);
nrf_resetinfo_restore_valid_set(NRF_RESETINFO, true);
nrf_lrcconf_poweron_force_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_DOMAIN_0, false);
nrf_lrcconf_poweron_force_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_MAIN, false);
suspend_common();
__set_BASEPRI(0);
__ISB();
__DSB();
__WFI();
/*
* We might reach this point is k_cpu_idle returns (there is a pre sleep hook that
* can abort sleeping.
*/
return -EBUSY;
}
static void do_suspend_to_ram(void)
{
/*
* Save the CPU context (including the return address),set the SRAM
* marker and power off the system.
*/
if (soc_s2ram_suspend(sys_suspend_to_ram)) {
return;
}
/*
* On resuming or error we return exactly *HERE*
*/
sys_resume();
}
#endif /* IS_ENABLED(CONFIG_PM_S2RAM) */
void pm_state_set(enum pm_state state, uint8_t substate_id)
{
if (state != PM_STATE_SUSPEND_TO_RAM) {
k_cpu_idle();
return;
}
#if IS_ENABLED(CONFIG_PM_S2RAM)
do_suspend_to_ram();
#endif
}
void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id)
{
irq_unlock(0);
}