1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #include <zephyr/arch/cpu.h>
7 #include <zephyr/arch/common/pm_s2ram.h>
8 #include <zephyr/linker/sections.h>
9 #include <zephyr/sys/util.h>
10 #include <hal/nrf_resetinfo.h>
11 #include "pm_s2ram.h"
12 
13 #include <cmsis_core.h>
14 
15 #define NVIC_MEMBER_SIZE(member) ARRAY_SIZE(((NVIC_Type *)0)->member)
16 
17 /* Currently dynamic regions are only used in case of userspace or stack guard and
18  * stack guard is not used by default on Cortex-M33 because there is a dedicated
19  * mechanism for stack overflow detection. Unless those condition change we don't
20  * need to store MPU content, it can just be reinitialized on resuming.
21  */
22 #define MPU_USE_DYNAMIC_REGIONS IS_ENABLED(CONFIG_USERSPACE) || IS_ENABLED(CONFIG_MPU_STACK_GUARD)
23 
24 /* TODO: The num-mpu-regions property should be used. Needs to be added to dts bindings. */
25 #define MPU_MAX_NUM_REGIONS 16
26 
27 typedef struct {
28 	/* NVIC components stored into RAM. */
29 	uint32_t ISER[NVIC_MEMBER_SIZE(ISER)];
30 	uint32_t ISPR[NVIC_MEMBER_SIZE(ISPR)];
31 	uint8_t IPR[NVIC_MEMBER_SIZE(IPR)];
32 } _nvic_context_t;
33 
34 typedef struct {
35 	uint32_t RNR;
36 	uint32_t RBAR[MPU_MAX_NUM_REGIONS];
37 	uint32_t RLAR[MPU_MAX_NUM_REGIONS];
38 	uint32_t MAIR0;
39 	uint32_t MAIR1;
40 	uint32_t CTRL;
41 } _mpu_context_t;
42 
43 typedef struct {
44 	uint32_t ICSR;
45 	uint32_t VTOR;
46 	uint32_t AIRCR;
47 	uint32_t SCR;
48 	uint32_t CCR;
49 	uint32_t SHPR[12U];
50 	uint32_t SHCSR;
51 	uint32_t CFSR;
52 	uint32_t HFSR;
53 	uint32_t DFSR;
54 	uint32_t MMFAR;
55 	uint32_t BFAR;
56 	uint32_t AFSR;
57 } _scb_context_t;
58 
59 struct backup {
60 	_nvic_context_t nvic_context;
61 	_mpu_context_t mpu_context;
62 	_scb_context_t scb_context;
63 };
64 
65 static __noinit struct backup backup_data;
66 
67 extern void z_arm_configure_static_mpu_regions(void);
68 extern int z_arm_mpu_init(void);
69 
70 /* MPU registers cannot be simply copied because content of RBARx RLARx registers
71  * depends on region which is selected by RNR register.
72  */
mpu_suspend(_mpu_context_t * backup)73 static void mpu_suspend(_mpu_context_t *backup)
74 {
75 	if (!MPU_USE_DYNAMIC_REGIONS) {
76 		return;
77 	}
78 
79 	backup->RNR = MPU->RNR;
80 
81 	for (uint8_t i = 0; i < MPU_MAX_NUM_REGIONS; i++) {
82 		MPU->RNR = i;
83 		backup->RBAR[i] = MPU->RBAR;
84 		backup->RLAR[i] = MPU->RLAR;
85 	}
86 	backup->MAIR0 = MPU->MAIR0;
87 	backup->MAIR1 = MPU->MAIR1;
88 	backup->CTRL = MPU->CTRL;
89 }
90 
mpu_resume(_mpu_context_t * backup)91 static void mpu_resume(_mpu_context_t *backup)
92 {
93 	if (!MPU_USE_DYNAMIC_REGIONS) {
94 		z_arm_mpu_init();
95 		z_arm_configure_static_mpu_regions();
96 		return;
97 	}
98 
99 	uint32_t rnr = backup->RNR;
100 
101 	for (uint8_t i = 0; i < MPU_MAX_NUM_REGIONS; i++) {
102 		MPU->RNR = i;
103 		MPU->RBAR = backup->RBAR[i];
104 		MPU->RLAR = backup->RLAR[i];
105 	}
106 
107 	MPU->MAIR0 = backup->MAIR0;
108 	MPU->MAIR1 = backup->MAIR1;
109 	MPU->RNR = rnr;
110 	MPU->CTRL = backup->CTRL;
111 }
112 
nvic_suspend(_nvic_context_t * backup)113 static void nvic_suspend(_nvic_context_t *backup)
114 {
115 	memcpy(backup->ISER, (uint32_t *)NVIC->ISER, sizeof(NVIC->ISER));
116 	memcpy(backup->ISPR, (uint32_t *)NVIC->ISPR, sizeof(NVIC->ISPR));
117 	memcpy(backup->IPR, (uint32_t *)NVIC->IPR, sizeof(NVIC->IPR));
118 }
119 
nvic_resume(_nvic_context_t * backup)120 static void nvic_resume(_nvic_context_t *backup)
121 {
122 	memcpy((uint32_t *)NVIC->ISER, backup->ISER, sizeof(NVIC->ISER));
123 	memcpy((uint32_t *)NVIC->ISPR, backup->ISPR, sizeof(NVIC->ISPR));
124 	memcpy((uint32_t *)NVIC->IPR, backup->IPR, sizeof(NVIC->IPR));
125 }
126 
scb_suspend(_scb_context_t * backup)127 static void scb_suspend(_scb_context_t *backup)
128 {
129 	backup->ICSR = SCB->ICSR;
130 	backup->VTOR = SCB->VTOR;
131 	backup->AIRCR = SCB->AIRCR;
132 	backup->SCR = SCB->SCR;
133 	backup->CCR = SCB->CCR;
134 	memcpy(backup->SHPR, (uint32_t *)SCB->SHPR, sizeof(SCB->SHPR));
135 	backup->SHCSR = SCB->SHCSR;
136 	backup->CFSR = SCB->CFSR;
137 	backup->HFSR = SCB->HFSR;
138 	backup->DFSR = SCB->DFSR;
139 	backup->MMFAR = SCB->MMFAR;
140 	backup->BFAR = SCB->BFAR;
141 	backup->AFSR = SCB->AFSR;
142 }
143 
scb_resume(_scb_context_t * backup)144 static void scb_resume(_scb_context_t *backup)
145 {
146 	SCB->ICSR = backup->ICSR;
147 	SCB->VTOR = backup->VTOR;
148 	SCB->AIRCR = backup->AIRCR;
149 	SCB->SCR = backup->SCR;
150 	SCB->CCR = backup->CCR;
151 	memcpy((uint32_t *)SCB->SHPR, backup->SHPR, sizeof(SCB->SHPR));
152 	SCB->SHCSR = backup->SHCSR;
153 	SCB->CFSR = backup->CFSR;
154 	SCB->HFSR = backup->HFSR;
155 	SCB->DFSR = backup->DFSR;
156 	SCB->MMFAR = backup->MMFAR;
157 	SCB->BFAR = backup->BFAR;
158 	SCB->AFSR = backup->AFSR;
159 }
160 
soc_s2ram_suspend(pm_s2ram_system_off_fn_t system_off)161 int soc_s2ram_suspend(pm_s2ram_system_off_fn_t system_off)
162 {
163 	int ret;
164 
165 	scb_suspend(&backup_data.scb_context);
166 	nvic_suspend(&backup_data.nvic_context);
167 	mpu_suspend(&backup_data.mpu_context);
168 	ret = arch_pm_s2ram_suspend(system_off);
169 	if (ret < 0) {
170 		return ret;
171 	}
172 
173 	mpu_resume(&backup_data.mpu_context);
174 	nvic_resume(&backup_data.nvic_context);
175 	scb_resume(&backup_data.scb_context);
176 
177 	return ret;
178 }
179 
pm_s2ram_mark_set(void)180 void __attribute__((naked)) pm_s2ram_mark_set(void)
181 {
182 	/* empty */
183 	__asm__ volatile("bx	lr\n");
184 }
185 
pm_s2ram_mark_check_and_clear(void)186 bool __attribute__((naked)) pm_s2ram_mark_check_and_clear(void)
187 {
188 	__asm__ volatile(
189 		/* Set return value to 0 */
190 		"mov	r0, #0\n"
191 
192 		/* Load and check RESETREAS register */
193 		"ldr	r3, [%[resetinfo_addr], %[resetreas_offs]]\n"
194 		"cmp	r3, %[resetreas_unretained_mask]\n"
195 
196 		"bne	exit\n"
197 
198 		/* Clear RESETREAS register */
199 		"str	r0, [%[resetinfo_addr], %[resetreas_offs]]\n"
200 
201 		/* Load RESTOREVALID register */
202 		"ldr	r3, [%[resetinfo_addr], %[restorevalid_offs]]\n"
203 
204 		/* Clear RESTOREVALID */
205 		"str	r0, [%[resetinfo_addr], %[restorevalid_offs]]\n"
206 
207 		/* Check RESTOREVALID register */
208 		"cmp	r3, %[restorevalid_present_mask]\n"
209 		"bne	exit\n"
210 
211 		/* Set return value to 1 */
212 		"mov	r0, #1\n"
213 
214 		"exit:\n"
215 		"bx	lr\n"
216 		:
217 		: [resetinfo_addr] "r"(NRF_RESETINFO),
218 		  [resetreas_offs] "r"(offsetof(NRF_RESETINFO_Type, RESETREAS.LOCAL)),
219 		  [resetreas_unretained_mask] "r"(NRF_RESETINFO_RESETREAS_LOCAL_UNRETAINED_MASK),
220 		  [restorevalid_offs] "r"(offsetof(NRF_RESETINFO_Type, RESTOREVALID)),
221 		  [restorevalid_present_mask] "r"(RESETINFO_RESTOREVALID_RESTOREVALID_Msk)
222 
223 		: "r0", "r1", "r3", "r4", "memory");
224 }
225