Skip to content

Commit fc514cd

Browse files
[nrf noup] arch: arm: cortex_m: Extend S2RAM support
Add preserving NVIC and MPU state in retained RAM when CPU is powered off during S2RAM procedure. Signed-off-by: Adam Kondraciuk <[email protected]>
1 parent aeff82d commit fc514cd

File tree

2 files changed

+135
-0
lines changed

2 files changed

+135
-0
lines changed

arch/arm/core/cortex_m/pm_s2ram.c

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,130 @@
99

1010
#include <zephyr/arch/common/pm_s2ram.h>
1111

12+
#define NVIC_MEMBER_SIZE(member) ARRAY_SIZE(((NVIC_Type *)0)->member)
13+
14+
/* Currently dynamic regions are only used in case of userspace or stack guard and
15+
* stack guard is not used by default on Cortex-M33 because there is a dedicated
16+
* mechanism for stack overflow detection. Unless those condition change we don't
17+
* need to store MPU content, it can just be reinitialized on resuming.
18+
*/
19+
#define MPU_USE_DYNAMIC_REGIONS IS_ENABLED(CONFIG_USERSPACE) || IS_ENABLED(CONFIG_MPU_STACK_GUARD)
20+
#define MPU_NUM_REGIONS DT_PROP(DT_INST(0, arm_armv8m_mpu), arm_num_mpu_regions)
21+
1222
#define MAGIC (0xDABBAD00)
1323

24+
typedef struct {
25+
/* NVIC components backuped into RAM. */
26+
uint32_t ISER[NVIC_MEMBER_SIZE(ISER)];
27+
uint32_t ISPR[NVIC_MEMBER_SIZE(ISPR)];
28+
uint32_t IABR[NVIC_MEMBER_SIZE(IABR)];
29+
uint8_t IPR[NVIC_MEMBER_SIZE(IPR)];
30+
} _nvic_context_t;
31+
32+
typedef struct {
33+
uint32_t RNR;
34+
uint32_t RBAR[MPU_NUM_REGIONS];
35+
uint32_t RLAR[MPU_NUM_REGIONS];
36+
uint32_t MAIR0;
37+
uint32_t MAIR1;
38+
uint32_t CTRL;
39+
} _mpu_context_t;
40+
1441
/**
1542
* CPU context for S2RAM
1643
*/
1744
__noinit _cpu_context_t _cpu_context;
1845

46+
/**
47+
* NVIC context for S2RAM
48+
*/
49+
50+
struct backup {
51+
_nvic_context_t nvic_context;
52+
_mpu_context_t mpu_context;
53+
};
54+
55+
static __noinit struct backup backup_data;
56+
57+
extern void z_arm_configure_static_mpu_regions(void);
58+
extern int z_arm_mpu_init(void);
59+
60+
/* MPU registers cannot be simply copied because content of RBARx RLARx registers
61+
* depends on region which is selected by RNR register.
62+
*/
63+
static void mpu_suspend(_mpu_context_t *backup)
64+
{
65+
if (!MPU_USE_DYNAMIC_REGIONS) {
66+
return;
67+
}
68+
69+
backup->RNR = MPU->RNR;
70+
71+
for (uint8_t i = 0; i < MPU_NUM_REGIONS; i++) {
72+
MPU->RNR = i;
73+
backup->RBAR[i] = MPU->RBAR;
74+
backup->RLAR[i] = MPU->RLAR;
75+
}
76+
backup->MAIR0 = MPU->MAIR0;
77+
backup->MAIR1 = MPU->MAIR1;
78+
backup->CTRL = MPU->CTRL;
79+
}
80+
81+
static void mpu_resume(_mpu_context_t *backup)
82+
{
83+
if (!MPU_USE_DYNAMIC_REGIONS) {
84+
z_arm_mpu_init();
85+
z_arm_configure_static_mpu_regions();
86+
return;
87+
}
88+
89+
uint32_t rnr = backup->RNR;
90+
91+
for (uint8_t i = 0; i < MPU_NUM_REGIONS; i++) {
92+
MPU->RNR = i;
93+
MPU->RBAR = backup->RBAR[i];
94+
MPU->RLAR = backup->RLAR[i];
95+
}
96+
97+
MPU->MAIR0 = backup->MAIR0;
98+
MPU->MAIR1 = backup->MAIR1;
99+
MPU->RNR = rnr;
100+
MPU->CTRL = backup->CTRL;
101+
}
102+
103+
static void nvic_suspend(_nvic_context_t *backup)
104+
{
105+
memcpy(backup->ISER, (uint32_t *)NVIC->ISER, sizeof(NVIC->ISER));
106+
memcpy(backup->ISPR, (uint32_t *)NVIC->ISPR, sizeof(NVIC->ISPR));
107+
memcpy(backup->IABR, (uint32_t *)NVIC->IABR, sizeof(NVIC->IABR));
108+
memcpy(backup->IPR, (uint32_t *)NVIC->IPR, sizeof(NVIC->IPR));
109+
}
110+
111+
static void nvic_resume(_nvic_context_t *backup)
112+
{
113+
memcpy((uint32_t *)NVIC->ISER, backup->ISER, sizeof(NVIC->ISER));
114+
memcpy((uint32_t *)NVIC->ISPR, backup->ISPR, sizeof(NVIC->ISPR));
115+
memcpy((uint32_t *)NVIC->IABR, backup->IABR, sizeof(NVIC->IABR));
116+
memcpy((uint32_t *)NVIC->IPR, backup->IPR, sizeof(NVIC->IPR));
117+
}
118+
119+
int pm_s2ram_suspend(pm_s2ram_system_off_fn_t system_off)
120+
{
121+
int ret;
122+
123+
nvic_suspend(&backup_data.nvic_context);
124+
mpu_suspend(&backup_data.mpu_context);
125+
ret = arch_pm_s2ram_suspend(system_off);
126+
if (ret < 0) {
127+
return ret;
128+
}
129+
130+
mpu_resume(&backup_data.mpu_context);
131+
nvic_resume(&backup_data.nvic_context);
132+
133+
return ret;
134+
}
135+
19136
#ifndef CONFIG_PM_S2RAM_CUSTOM_MARKING
20137
/**
21138
* S2RAM Marker

include/zephyr/arch/common/pm_s2ram.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,24 @@ typedef int (*pm_s2ram_system_off_fn_t)(void);
5656
*/
5757
int arch_pm_s2ram_suspend(pm_s2ram_system_off_fn_t system_off);
5858

59+
/**
60+
* @brief Save CPU state on suspend
61+
*
62+
* This function is used on suspend-to-RAM (S2RAM) to save the CPU state in
63+
* (retained) RAM before powering the system off using the provided function.
64+
* This function is usually called from the PM subsystem / hooks.
65+
*
66+
* The CPU state consist of internal registers and peripherals like
67+
* interrupt controller, memory controllers, etc.
68+
*
69+
* @param system_off Function to power off the system.
70+
*
71+
* @retval 0 The CPU context was successfully saved and restored.
72+
* @retval -EBUSY The system is busy and cannot be suspended at this time.
73+
* @retval -errno Negative errno code in case of failure.
74+
*/
75+
int pm_s2ram_suspend(pm_s2ram_system_off_fn_t system_off);
76+
5977
/**
6078
* @brief Mark that core is entering suspend-to-RAM state.
6179
*

0 commit comments

Comments
 (0)