Skip to content
176 changes: 15 additions & 161 deletions soc/nordic/nrf54h/pm_s2ram.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
*/

#include <zephyr/arch/cpu.h>
#include <zephyr/arch/arm/mpu/arm_mpu.h>
#include <zephyr/arch/arm/cortex_m/scb.h>
#include <zephyr/arch/arm/cortex_m/fpu.h>
#include <zephyr/arch/common/pm_s2ram.h>
#include <zephyr/linker/sections.h>
#include <zephyr/sys/util.h>
Expand All @@ -22,119 +25,26 @@
#define SCnSCB_CPPWR_SU10_Pos 20U /*!< CPPWR: SU10 Position */
#define SCnSCB_CPPWR_SU10_Msk (1UL << SCnSCB_CPPWR_SU10_Pos) /*!< CPPWR: SU10 Mask */

/* Currently dynamic regions are only used in case of userspace or stack guard and
* stack guard is not used by default on Cortex-M33 because there is a dedicated
* mechanism for stack overflow detection. Unless those condition change we don't
* need to store MPU content, it can just be reinitialized on resuming.
*/
#define MPU_USE_DYNAMIC_REGIONS IS_ENABLED(CONFIG_USERSPACE) || IS_ENABLED(CONFIG_MPU_STACK_GUARD)

/* TODO: The num-mpu-regions property should be used. Needs to be added to dts bindings. */
#define MPU_MAX_NUM_REGIONS 16

typedef struct {
/* NVIC components stored into RAM. */
uint32_t ISER[NVIC_MEMBER_SIZE(ISER)];
uint32_t ISPR[NVIC_MEMBER_SIZE(ISPR)];
uint8_t IPR[NVIC_MEMBER_SIZE(IPR)];
} _nvic_context_t;

typedef struct {
uint32_t RNR;
uint32_t RBAR[MPU_MAX_NUM_REGIONS];
uint32_t RLAR[MPU_MAX_NUM_REGIONS];
uint32_t MAIR0;
uint32_t MAIR1;
uint32_t CTRL;
} _mpu_context_t;

typedef struct {
uint32_t ICSR;
uint32_t VTOR;
uint32_t AIRCR;
uint32_t SCR;
uint32_t CCR;
uint32_t SHPR[12U];
uint32_t SHCSR;
uint32_t CFSR;
uint32_t HFSR;
uint32_t DFSR;
uint32_t MMFAR;
uint32_t BFAR;
uint32_t AFSR;
uint32_t CPACR;
} _scb_context_t;

#if defined(CONFIG_FPU) && !defined(CONFIG_FPU_SHARING)
typedef struct {
uint32_t FPCCR;
uint32_t FPCAR;
uint32_t FPDSCR;
uint32_t S[32];
} _fpu_context_t;
#endif

struct backup {
_nvic_context_t nvic_context;
#if defined(CONFIG_MPU)
_mpu_context_t mpu_context;
#if defined(CONFIG_ARM_MPU)
struct z_mpu_context_retained mpu_context;
#endif
_scb_context_t scb_context;
struct scb_context scb_context;
#if defined(CONFIG_FPU) && !defined(CONFIG_FPU_SHARING)
_fpu_context_t fpu_context;
struct fpu_ctx_full fpu_context;
#endif
};

static __noinit struct backup backup_data;

extern void z_arm_configure_static_mpu_regions(void);
extern int z_arm_mpu_init(void);

#if defined(CONFIG_MPU)
/* MPU registers cannot be simply copied because content of RBARx RLARx registers
* depends on region which is selected by RNR register.
*/
static void mpu_save(_mpu_context_t *backup)
{
if (!MPU_USE_DYNAMIC_REGIONS) {
return;
}

backup->RNR = MPU->RNR;

for (uint8_t i = 0; i < MPU_MAX_NUM_REGIONS; i++) {
MPU->RNR = i;
backup->RBAR[i] = MPU->RBAR;
backup->RLAR[i] = MPU->RLAR;
}
backup->MAIR0 = MPU->MAIR0;
backup->MAIR1 = MPU->MAIR1;
backup->CTRL = MPU->CTRL;
}

static void mpu_restore(_mpu_context_t *backup)
{
if (!MPU_USE_DYNAMIC_REGIONS) {
z_arm_mpu_init();
z_arm_configure_static_mpu_regions();
return;
}

uint32_t rnr = backup->RNR;

for (uint8_t i = 0; i < MPU_MAX_NUM_REGIONS; i++) {
MPU->RNR = i;
MPU->RBAR = backup->RBAR[i];
MPU->RLAR = backup->RLAR[i];
}

MPU->MAIR0 = backup->MAIR0;
MPU->MAIR1 = backup->MAIR1;
MPU->RNR = rnr;
MPU->CTRL = backup->CTRL;
}
#endif /* defined(CONFIG_MPU) */

static void nvic_save(_nvic_context_t *backup)
{
memcpy(backup->ISER, (uint32_t *)NVIC->ISER, sizeof(NVIC->ISER));
Expand All @@ -149,42 +59,6 @@ static void nvic_restore(_nvic_context_t *backup)
memcpy((uint32_t *)NVIC->IPR, backup->IPR, sizeof(NVIC->IPR));
}

static void scb_save(_scb_context_t *backup)
{
backup->ICSR = SCB->ICSR;
backup->VTOR = SCB->VTOR;
backup->AIRCR = SCB->AIRCR;
backup->SCR = SCB->SCR;
backup->CCR = SCB->CCR;
memcpy(backup->SHPR, (uint32_t *)SCB->SHPR, sizeof(SCB->SHPR));
backup->SHCSR = SCB->SHCSR;
backup->CFSR = SCB->CFSR;
backup->HFSR = SCB->HFSR;
backup->DFSR = SCB->DFSR;
backup->MMFAR = SCB->MMFAR;
backup->BFAR = SCB->BFAR;
backup->AFSR = SCB->AFSR;
backup->CPACR = SCB->CPACR;
}

static void scb_restore(_scb_context_t *backup)
{
SCB->ICSR = backup->ICSR;
SCB->VTOR = backup->VTOR;
SCB->AIRCR = backup->AIRCR;
SCB->SCR = backup->SCR;
SCB->CCR = backup->CCR;
memcpy((uint32_t *)SCB->SHPR, backup->SHPR, sizeof(SCB->SHPR));
SCB->SHCSR = backup->SHCSR;
SCB->CFSR = backup->CFSR;
SCB->HFSR = backup->HFSR;
SCB->DFSR = backup->DFSR;
SCB->MMFAR = backup->MMFAR;
SCB->BFAR = backup->BFAR;
SCB->AFSR = backup->AFSR;
SCB->CPACR = backup->CPACR;
}

#if defined(CONFIG_FPU)
static void fpu_power_down(void)
{
Expand All @@ -201,26 +75,6 @@ static void fpu_power_up(void)
__DSB();
__ISB();
}

#if !defined(CONFIG_FPU_SHARING)
static void fpu_save(_fpu_context_t *backup)
{
backup->FPCCR = FPU->FPCCR;
backup->FPCAR = FPU->FPCAR;
backup->FPDSCR = FPU->FPDSCR;

__asm__ volatile("vstmia %0, {s0-s31}\n" : : "r"(backup->S) : "memory");
}

static void fpu_restore(_fpu_context_t *backup)
{
FPU->FPCCR = backup->FPCCR;
FPU->FPCAR = backup->FPCAR;
FPU->FPDSCR = backup->FPDSCR;

__asm__ volatile("vldmia %0, {s0-s31}\n" : : "r"(backup->S) : "memory");
}
#endif /* !defined(CONFIG_FPU_SHARING) */
#endif /* defined(CONFIG_FPU) */

#if DT_NODE_EXISTS(DT_NODELABEL(mcuboot_s2ram)) &&\
Expand All @@ -240,17 +94,17 @@ int soc_s2ram_suspend(pm_s2ram_system_off_fn_t system_off)
{
int ret;

z_arm_save_scb_context(&backup_data.scb_context);
SET_MCUBOOT_RESUME_MAGIC();
scb_save(&backup_data.scb_context);
#if defined(CONFIG_FPU)
#if !defined(CONFIG_FPU_SHARING)
fpu_save(&backup_data.fpu_context);
z_arm_save_fp_context(&backup_data.fpu_context);
#endif
fpu_power_down();
#endif
nvic_save(&backup_data.nvic_context);
#if defined(CONFIG_MPU)
mpu_save(&backup_data.mpu_context);
#if defined(CONFIG_ARM_MPU)
z_arm_save_mpu_context(&backup_data.mpu_context);
#endif
ret = arch_pm_s2ram_suspend(system_off);
/* Cache and FPU are powered down so power up is needed even if s2ram failed. */
Expand All @@ -259,18 +113,18 @@ int soc_s2ram_suspend(pm_s2ram_system_off_fn_t system_off)
fpu_power_up();
#if !defined(CONFIG_FPU_SHARING)
/* Also the FPU content might be lost. */
fpu_restore(&backup_data.fpu_context);
z_arm_restore_fp_context(&backup_data.fpu_context);
#endif
#endif
if (ret < 0) {
return ret;
}

#if defined(CONFIG_MPU)
mpu_restore(&backup_data.mpu_context);
#if defined(CONFIG_ARM_MPU)
z_arm_restore_mpu_context(&backup_data.mpu_context);
#endif
nvic_restore(&backup_data.nvic_context);
scb_restore(&backup_data.scb_context);
z_arm_restore_scb_context(&backup_data.scb_context);

return ret;
}
Expand Down